Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomáš Rylek <trylek@microsoft.com>2020-12-08 05:19:44 +0300
committerGitHub <noreply@github.com>2020-12-08 05:19:44 +0300
commit69e114c1abf91241a0eeecf1ecceab4711b8aa62 (patch)
treeb81a0b35748f5e598412bcc504335cdbd322cd43 /src/coreclr/utilcode
parent0ec07945a9759a72a689edbb01e69b232e26e05a (diff)
December infra rollout - remove duplicated 'src' from coreclr subrepo (src/coreclr/src becomes src/coreclr) (#44973)
* Move src/coreclr/src/Directory.Build.targets to src/coreclr Merge src/coreclr/src/CMakeLists.txt into src/coreclr/CMakeLists.txt * Mechanical move of src/coreclr/src to src/coreclr * Scripts adjustments to reflect the changed paths
Diffstat (limited to 'src/coreclr/utilcode')
-rw-r--r--src/coreclr/utilcode/CMakeLists.txt130
-rw-r--r--src/coreclr/utilcode/arraylist.cpp224
-rw-r--r--src/coreclr/utilcode/bitvector.cpp405
-rw-r--r--src/coreclr/utilcode/ccomprc.cpp725
-rw-r--r--src/coreclr/utilcode/check.cpp281
-rw-r--r--src/coreclr/utilcode/clrconfig.cpp385
-rw-r--r--src/coreclr/utilcode/clrhelpers.cpp12
-rw-r--r--src/coreclr/utilcode/clrhost.cpp249
-rw-r--r--src/coreclr/utilcode/clrhost_nodependencies.cpp572
-rw-r--r--src/coreclr/utilcode/collections.cpp970
-rw-r--r--src/coreclr/utilcode/comex.cpp50
-rw-r--r--src/coreclr/utilcode/configuration.cpp135
-rw-r--r--src/coreclr/utilcode/corimage.cpp259
-rw-r--r--src/coreclr/utilcode/cycletimer.cpp84
-rw-r--r--src/coreclr/utilcode/dacutil.cpp246
-rw-r--r--src/coreclr/utilcode/debug.cpp841
-rw-r--r--src/coreclr/utilcode/dlwrap.cpp77
-rw-r--r--src/coreclr/utilcode/ex.cpp1358
-rw-r--r--src/coreclr/utilcode/format1.cpp119
-rw-r--r--src/coreclr/utilcode/fstream.cpp294
-rw-r--r--src/coreclr/utilcode/fstring.cpp321
-rw-r--r--src/coreclr/utilcode/guidfromname.cpp235
-rw-r--r--src/coreclr/utilcode/hostimpl.cpp82
-rw-r--r--src/coreclr/utilcode/iallocator.cpp9
-rw-r--r--src/coreclr/utilcode/ilformatter.cpp818
-rw-r--r--src/coreclr/utilcode/loaderheap.cpp2254
-rw-r--r--src/coreclr/utilcode/log.cpp420
-rw-r--r--src/coreclr/utilcode/longfilepathwrappers.cpp959
-rw-r--r--src/coreclr/utilcode/makepath.cpp212
-rw-r--r--src/coreclr/utilcode/md5.cpp308
-rw-r--r--src/coreclr/utilcode/memorypool.cpp316
-rw-r--r--src/coreclr/utilcode/namespaceutil.cpp678
-rw-r--r--src/coreclr/utilcode/opinfo.cpp121
-rw-r--r--src/coreclr/utilcode/outstring.cpp180
-rw-r--r--src/coreclr/utilcode/pedecoder.cpp3280
-rw-r--r--src/coreclr/utilcode/peinformation.cpp98
-rw-r--r--src/coreclr/utilcode/posterror.cpp339
-rw-r--r--src/coreclr/utilcode/prettyprintsig.cpp1027
-rw-r--r--src/coreclr/utilcode/regutil.cpp759
-rw-r--r--src/coreclr/utilcode/safewrap.cpp336
-rw-r--r--src/coreclr/utilcode/sbuffer.cpp145
-rw-r--r--src/coreclr/utilcode/securityutil.cpp494
-rw-r--r--src/coreclr/utilcode/securitywrapper.cpp749
-rw-r--r--src/coreclr/utilcode/sha1.cpp368
-rw-r--r--src/coreclr/utilcode/sigbuilder.cpp164
-rw-r--r--src/coreclr/utilcode/sigparser.cpp194
-rw-r--r--src/coreclr/utilcode/splitpath.cpp258
-rw-r--r--src/coreclr/utilcode/sstring.cpp2790
-rw-r--r--src/coreclr/utilcode/sstring_com.cpp101
-rw-r--r--src/coreclr/utilcode/stacktrace.cpp991
-rw-r--r--src/coreclr/utilcode/stdafx.h21
-rw-r--r--src/coreclr/utilcode/stgpool.cpp2423
-rw-r--r--src/coreclr/utilcode/stgpooli.cpp347
-rw-r--r--src/coreclr/utilcode/stgpoolreadonly.cpp210
-rw-r--r--src/coreclr/utilcode/stresslog.cpp684
-rw-r--r--src/coreclr/utilcode/util.cpp3292
-rw-r--r--src/coreclr/utilcode/util_nodependencies.cpp814
-rw-r--r--src/coreclr/utilcode/utilmessagebox.cpp412
-rw-r--r--src/coreclr/utilcode/utsem.cpp505
-rw-r--r--src/coreclr/utilcode/winfix.cpp300
-rw-r--r--src/coreclr/utilcode/yieldprocessornormalized.cpp9
61 files changed, 35439 insertions, 0 deletions
diff --git a/src/coreclr/utilcode/CMakeLists.txt b/src/coreclr/utilcode/CMakeLists.txt
new file mode 100644
index 00000000000..c7c5861f129
--- /dev/null
+++ b/src/coreclr/utilcode/CMakeLists.txt
@@ -0,0 +1,130 @@
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(UTILCODE_COMMON_SOURCES
+ clrhost_nodependencies.cpp
+ ccomprc.cpp
+ ex.cpp
+ sbuffer.cpp
+ sstring_com.cpp
+ fstring.cpp
+ namespaceutil.cpp
+ makepath.cpp
+ splitpath.cpp
+ clrconfig.cpp
+ configuration.cpp
+ collections.cpp
+ posterror.cpp
+ fstream.cpp
+ clrhelpers.cpp
+ stgpool.cpp
+ stgpooli.cpp
+ stgpoolreadonly.cpp
+ utsem.cpp
+ peinformation.cpp
+ check.cpp
+ log.cpp
+ arraylist.cpp
+ bitvector.cpp
+ comex.cpp
+ guidfromname.cpp
+ memorypool.cpp
+ iallocator.cpp
+ loaderheap.cpp
+ outstring.cpp
+ ilformatter.cpp
+ opinfo.cpp
+ corimage.cpp
+ format1.cpp
+ prettyprintsig.cpp
+ regutil.cpp
+ sha1.cpp
+ sigbuilder.cpp
+ sigparser.cpp
+ sstring.cpp
+ util_nodependencies.cpp
+ utilmessagebox.cpp
+ safewrap.cpp
+ clrhost.cpp
+ cycletimer.cpp
+ md5.cpp
+ util.cpp
+ stresslog.cpp
+ debug.cpp
+ pedecoder.cpp
+ winfix.cpp
+ longfilepathwrappers.cpp
+ yieldprocessornormalized.cpp
+)
+
+# These source file do not yet compile on Linux.
+# They should be moved out from here into the declaration
+# of UTILCODE_SOURCES above after fixing compiler errors.
+if(CLR_CMAKE_TARGET_WIN32)
+ list(APPEND UTILCODE_COMMON_SOURCES
+ dacutil.cpp
+ dlwrap.cpp
+ securitywrapper.cpp
+ securityutil.cpp
+ stacktrace.cpp
+ )
+endif(CLR_CMAKE_TARGET_WIN32)
+
+set(UTILCODE_SOURCES
+ ${UTILCODE_COMMON_SOURCES}
+)
+
+set(UTILCODE_DAC_SOURCES
+ ${UTILCODE_COMMON_SOURCES}
+ hostimpl.cpp
+)
+
+set(UTILCODE_CROSSGEN_SOURCES
+ ${UTILCODE_COMMON_SOURCES}
+ hostimpl.cpp
+)
+
+set(UTILCODE_STATICNOHOST_SOURCES
+ ${UTILCODE_COMMON_SOURCES}
+ hostimpl.cpp
+)
+
+set (UTILCODE_DEPENDENCIES eventing_headers)
+
+convert_to_absolute_path(UTILCODE_SOURCES ${UTILCODE_SOURCES})
+convert_to_absolute_path(UTILCODE_DAC_SOURCES ${UTILCODE_DAC_SOURCES})
+convert_to_absolute_path(UTILCODE_CROSSGEN_SOURCES ${UTILCODE_CROSSGEN_SOURCES})
+convert_to_absolute_path(UTILCODE_STATICNOHOST_SOURCES ${UTILCODE_STATICNOHOST_SOURCES})
+
+add_library_clr(utilcode_dac STATIC ${UTILCODE_DAC_SOURCES})
+add_library_clr(utilcode_obj OBJECT ${UTILCODE_SOURCES})
+add_library(utilcode INTERFACE)
+target_sources(utilcode INTERFACE $<TARGET_OBJECTS:utilcode_obj>)
+add_library_clr(utilcodestaticnohost STATIC ${UTILCODE_STATICNOHOST_SOURCES})
+add_library_clr(utilcode_crossgen STATIC ${UTILCODE_CROSSGEN_SOURCES})
+
+if(CLR_CMAKE_HOST_UNIX)
+ target_link_libraries(utilcodestaticnohost nativeresourcestring)
+ target_link_libraries(utilcode_crossgen nativeresourcestring)
+ target_link_libraries(utilcode_dac nativeresourcestring)
+ target_link_libraries(utilcode INTERFACE nativeresourcestring)
+ add_dependencies(utilcode_dac coreclrpal)
+ add_dependencies(utilcode_obj coreclrpal)
+endif(CLR_CMAKE_HOST_UNIX)
+
+
+if(CLR_CMAKE_HOST_WIN32)
+ target_compile_definitions(utilcodestaticnohost PRIVATE _CRTIMP=) # use static version of crt
+endif(CLR_CMAKE_HOST_WIN32)
+
+set_target_properties(utilcode_dac PROPERTIES DAC_COMPONENT TRUE)
+set_target_properties(utilcode_crossgen PROPERTIES CROSSGEN_COMPONENT TRUE)
+target_compile_definitions(utilcode_dac PRIVATE SELF_NO_HOST)
+target_compile_definitions(utilcodestaticnohost PRIVATE SELF_NO_HOST)
+add_dependencies(utilcode_dac ${UTILCODE_DEPENDENCIES})
+add_dependencies(utilcode_obj ${UTILCODE_DEPENDENCIES})
+add_dependencies(utilcode_crossgen ${UTILCODE_DEPENDENCIES})
+add_dependencies(utilcodestaticnohost ${UTILCODE_DEPENDENCIES})
+target_precompile_headers(utilcode_dac PRIVATE [["stdafx.h"]])
+target_precompile_headers(utilcode_obj PRIVATE [["stdafx.h"]])
+target_precompile_headers(utilcode_crossgen PRIVATE [["stdafx.h"]])
+target_precompile_headers(utilcodestaticnohost PRIVATE [["stdafx.h"]])
diff --git a/src/coreclr/utilcode/arraylist.cpp b/src/coreclr/utilcode/arraylist.cpp
new file mode 100644
index 00000000000..c6e538fd38d
--- /dev/null
+++ b/src/coreclr/utilcode/arraylist.cpp
@@ -0,0 +1,224 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+#include "arraylist.h"
+#include "utilcode.h"
+#include "ex.h"
+
+//
+// ArrayList is a simple class which is used to contain a growable
+// list of pointers, stored in chunks. Modification is by appending
+// only currently. Access is by index (efficient if the number of
+// elements stays small) and iteration (efficient in all cases).
+//
+// An important property of an ArrayList is that the list remains
+// coherent while it is being modified (appended to). This means that readers
+// never need to lock when accessing it. (Locking is necessary among multiple
+// writers, however.)
+//
+
+void ArrayListBase::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ ArrayListBlock *block = m_firstBlock.m_next;
+ while (block != NULL)
+ {
+ ArrayListBlock *next = block->m_next;
+ delete [] block;
+ block = next;
+ }
+ m_firstBlock.m_next = 0;
+ m_count = 0;
+}
+
+PTR_VOID * ArrayListBase::GetPtr(DWORD index) const
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+
+ _ASSERTE(index < m_count);
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+ while (index >= b->m_blockSize)
+ {
+ PREFIX_ASSUME(b->m_next != NULL);
+ index -= b->m_blockSize;
+ b = b->m_next;
+ }
+
+ return b->m_array + index;
+}
+
+#ifndef DACCESS_COMPILE
+
+HRESULT ArrayListBase::Append(void *element)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+ DWORD count = m_count;
+
+ while (count >= b->m_blockSize)
+ {
+ count -= b->m_blockSize;
+
+ if (b->m_next == NULL)
+ {
+ _ASSERTE(count == 0);
+
+ DWORD nextSize = b->m_blockSize * 2;
+
+ ArrayListBlock *bNew = (ArrayListBlock *)
+ new (nothrow) BYTE [sizeof(ArrayListBlock) + nextSize * sizeof(void*)];
+
+ if (bNew == NULL)
+ return E_OUTOFMEMORY;
+
+ bNew->m_next = NULL;
+ bNew->m_blockSize = nextSize;
+
+ b->m_next = bNew;
+ }
+
+ b = b->m_next;
+ }
+
+ b->m_array[count] = element;
+
+ m_count++;
+
+ return S_OK;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+DWORD ArrayListBase::FindElement(DWORD start, PTR_VOID element) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ DWORD index = start;
+
+ _ASSERTE(index <= m_count);
+
+ ArrayListBlock *b = (ArrayListBlock*)&m_firstBlock;
+
+ //
+ // Skip to the block containing start.
+ // index should be the index of start in the block.
+ //
+
+ while (b != NULL && index >= b->m_blockSize)
+ {
+ index -= b->m_blockSize;
+ b = b->m_next;
+ }
+
+ //
+ // Adjust start to be the index of the start of the block
+ //
+
+ start -= index;
+
+ //
+ // Compute max number of entries from the start of the block
+ //
+
+ DWORD max = m_count - start;
+
+ while (b != NULL)
+ {
+ //
+ // Compute end of search in this block - either end of the block
+ // or end of the array
+ //
+
+ DWORD blockMax;
+ if (max < b->m_blockSize)
+ blockMax = max;
+ else
+ blockMax = b->m_blockSize;
+
+ //
+ // Scan for element, until the end.
+ //
+
+ while (index < blockMax)
+ {
+ if (b->m_array[index] == element)
+ return start + index;
+ index++;
+ }
+
+ //
+ // Otherwise, increment block start index, decrement max count,
+ // reset index, and go to the next block (if any)
+ //
+
+ start += b->m_blockSize;
+ max -= b->m_blockSize;
+ index = 0;
+ b = b->m_next;
+ }
+
+ return (DWORD) NOT_FOUND;
+}
+
+BOOL ArrayListBase::Iterator::Next()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ ++m_index;
+
+ if (m_index >= m_remaining)
+ return FALSE;
+
+ if (m_index >= m_block->m_blockSize)
+ {
+ m_remaining -= m_block->m_blockSize;
+ m_index -= m_block->m_blockSize;
+ m_total += m_block->m_blockSize;
+ m_block = m_block->m_next;
+ }
+
+ return TRUE;
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+ArrayListBase::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+
+ // Assume that 'this' is enumerated, either explicitly
+ // or because this class is embedded in another.
+
+ PTR_ArrayListBlock block = m_firstBlock.m_next;
+ while (block.IsValid())
+ {
+ block.EnumMem();
+ block = block->m_next;
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
diff --git a/src/coreclr/utilcode/bitvector.cpp b/src/coreclr/utilcode/bitvector.cpp
new file mode 100644
index 00000000000..b4c53aa96ed
--- /dev/null
+++ b/src/coreclr/utilcode/bitvector.cpp
@@ -0,0 +1,405 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/***************************************************************************/
+/* BitVector.cpp */
+/***************************************************************************/
+// Routines to support a growable bitvector
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <memory.h>
+
+#include "utilcode.h"
+
+#include "bitvector.h"
+
+#if USE_BITVECTOR
+
+int BitVector::NumBits() const
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ int count = 0;
+ ChunkType hiChunk;
+
+ if (isBig())
+ {
+ unsigned maxNonZero = 0;
+ for (unsigned i=1; (i < m_vals.GetLength()); i++)
+ {
+ if (m_vals.m_chunks[i] != 0)
+ {
+ maxNonZero = i;
+ }
+ }
+ count = (maxNonZero * CHUNK_BITS) - 1;
+ hiChunk = m_vals.m_chunks[maxNonZero];
+ }
+ else
+ {
+ hiChunk = m_val;
+ }
+
+ while (hiChunk > 0)
+ {
+ hiChunk <<= 1;
+ count++;
+ }
+
+ _ASSERTE(count >= 0);
+ return count;
+}
+
+void BitVector::doBigInit(ChunkType arg)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ m_vals.m_chunks[0] = arg;
+ m_vals.SetLength(1);
+}
+
+void BitVector::doBigInit(const BitVector& arg)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ if (arg.isBig())
+ {
+ memcpy(m_vals.m_chunks, arg.m_vals.m_chunks, (sizeof(ChunkType) * arg.m_vals.GetLength()));
+ m_vals.SetLength(arg.m_vals.GetLength());
+ }
+ else
+ {
+ m_val = arg.m_val;
+ }
+}
+
+void BitVector::doBigLeftShiftAssign(unsigned shift)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything
+ return;
+
+ unsigned numWords = shift / CHUNK_BITS;
+ unsigned numBits = shift % CHUNK_BITS;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ int from = m_vals.GetLength()-1;
+ int to = from + numWords;
+ unsigned newlen = to + 1;
+
+ ChunkType topBits = 0;
+ if (numBits > 0)
+ {
+ topBits = m_vals.m_chunks[from] >> (CHUNK_BITS - numBits);
+ }
+
+ if (topBits != 0 || numWords != 0)
+ {
+ if (topBits != 0)
+ {
+ m_vals.m_chunks[newlen] = topBits;
+ newlen++;
+ }
+ m_vals.SetLength(newlen);
+ }
+
+ while (to >= 0)
+ {
+ m_vals.m_chunks[to] = (from >= 0) ? (m_vals.m_chunks[from] << numBits) : 0;
+ from--;
+
+ if ((from >= 0) && (numBits > 0))
+ {
+ m_vals.m_chunks[to] |= m_vals.m_chunks[from] >> (CHUNK_BITS - numBits);
+ }
+ to--;
+ }
+
+ // Convert back to small format if necessary
+ if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal))
+ {
+ m_val = ChunkType(m_vals.m_chunks[0] << 1);
+ }
+}
+
+void BitVector::doBigRightShiftAssign(unsigned shift)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ if ((m_val == 0) || (shift == 0)) // Zero is a special case, don't need to do anything
+ return;
+
+ unsigned numWords = shift / CHUNK_BITS;
+ unsigned numBits = shift % CHUNK_BITS;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ unsigned from = numWords;
+ unsigned to = 0;
+ unsigned len = m_vals.GetLength();
+ unsigned newlen = len - numWords;
+
+ if (from >= len)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+ else
+ {
+ m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits);
+ from++;
+
+ while (from < len)
+ {
+ if (numBits > 0)
+ {
+ m_vals.m_chunks[to] |= m_vals.m_chunks[from] << (CHUNK_BITS - numBits);
+ }
+ to++;
+
+ m_vals.m_chunks[to] = (m_vals.m_chunks[from] >> numBits);
+ from++;
+ }
+
+ if ((newlen > 1) && (m_vals.m_chunks[newlen-1] == 0))
+ {
+ newlen--;
+ }
+
+ m_vals.SetLength(newlen);
+
+ // Convert back to small format if necessary
+ if ((newlen == 1) && (m_vals.m_chunks[0] <= MaxVal))
+ {
+ m_val = ChunkType(m_vals.m_chunks[0] << 1);
+ }
+ }
+}
+
+void BitVector::doBigAndAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ if (arg.isBig())
+ {
+ bool isZero = true; // until proven otherwise
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+
+ if (myLen > argLen)
+ {
+ // shrink our length to match argLen
+ m_vals.SetLength(argLen);
+ myLen = argLen;
+ }
+
+ for (unsigned i = 0; (i < myLen); i++)
+ {
+ ChunkType curChunk = m_vals.m_chunks[i] & arg.m_vals.m_chunks[i];
+ m_vals.m_chunks[i] = curChunk;
+ if (curChunk != 0)
+ isZero = false;
+ }
+
+ if (isZero)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+ }
+ else
+ {
+ m_val = (m_vals.m_chunks[0] << 1) & arg.m_val;
+ }
+}
+
+void BitVector::doBigOrAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ if (arg.isBig())
+ {
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+
+ if (myLen < argLen)
+ {
+ // expand our length to match argLen and zero init
+ memset(m_vals.m_chunks + myLen, 0, sizeof(ChunkType) * (argLen - myLen));
+ m_vals.SetLength(argLen);
+ myLen = argLen;
+ }
+
+ for(unsigned i = 0; ((i < myLen) && (i < argLen)); i++)
+ {
+ m_vals.m_chunks[i] |= arg.m_vals.m_chunks[i];
+ }
+ }
+ else
+ {
+ m_vals.m_chunks[0] |= arg.smallBits();
+ }
+}
+
+void BitVector::doBigDiffAssign(const BitVector& arg)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Change to Big representation
+ //
+ toBig();
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ bool isZero = true; // until proven otherwise
+
+ for (unsigned i = 0; (i < myLen); i++)
+ {
+ ChunkType nextChunk = m_vals.m_chunks[i];
+ if (i < argLen)
+ {
+ nextChunk &= ~arg.m_vals.m_chunks[i];
+ m_vals.m_chunks[i] = nextChunk;
+ }
+ else if (i == 0)
+ {
+ nextChunk &= ~arg.smallBits();
+ m_vals.m_chunks[i] = nextChunk;
+ }
+
+ if (nextChunk != 0)
+ isZero = false;
+ }
+
+ if (isZero)
+ {
+ // we always encode zero in short form
+ m_val = 0;
+ }
+}
+
+BOOL BitVector::doBigEquals(const BitVector& arg) const
+{
+ CONTRACT(BOOL)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ unsigned maxLen = (myLen >= argLen) ? myLen : argLen;
+
+ for (unsigned i=0; (i < maxLen); i++)
+ {
+ ChunkType myVal = 0;
+ ChunkType argVal = 0;
+
+ if (i < myLen)
+ myVal = m_vals.m_chunks[i];
+
+ if (i < argLen)
+ argVal = arg.m_vals.m_chunks[i];
+
+ if (i == 0)
+ {
+ if (myLen == 0)
+ myVal = smallBits();
+ if (argLen == 0)
+ argVal = arg.smallBits();
+ }
+
+ if (myVal != argVal)
+ RETURN false;
+ }
+ RETURN true;
+}
+
+BOOL BitVector::doBigIntersect(const BitVector& arg) const
+{
+ CONTRACT(BOOL)
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ unsigned myLen = m_vals.GetLength();
+ unsigned argLen = arg.m_vals.GetLength();
+ unsigned minLen = (myLen <= argLen) ? myLen : argLen;
+
+ for (unsigned i=0; (i <= minLen); i++)
+ {
+ ChunkType myVal = 0;
+ ChunkType argVal = 0;
+
+ if (i < myLen)
+ myVal = m_vals.m_chunks[i];
+
+ if (i < argLen)
+ argVal = arg.m_vals.m_chunks[i];
+
+ if (i == 0)
+ {
+ if (myLen == 0)
+ myVal = smallBits();
+ if (argLen == 0)
+ argVal = arg.smallBits();
+ }
+
+ if ((myVal & argVal) != 0)
+ RETURN true;
+ }
+ RETURN false;
+}
+
+#endif // USE_BITVECTOR
diff --git a/src/coreclr/utilcode/ccomprc.cpp b/src/coreclr/utilcode/ccomprc.cpp
new file mode 100644
index 00000000000..1b38edf419e
--- /dev/null
+++ b/src/coreclr/utilcode/ccomprc.cpp
@@ -0,0 +1,725 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h" // Standard header.
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+
+#include "../dlls/mscorrc/resource.h"
+#ifdef HOST_UNIX
+#include "resourcestring.h"
+#define NATIVE_STRING_RESOURCE_NAME mscorrc
+__attribute__((visibility("default"))) DECLARE_NATIVE_STRING_RESOURCE_TABLE(NATIVE_STRING_RESOURCE_NAME);
+#endif
+#include "sstring.h"
+#include "stringarraylist.h"
+#include "corpriv.h"
+
+#include <stdlib.h>
+
+// External prototypes.
+extern void* GetClrModuleBase();
+
+//*****************************************************************************
+// Do the mapping from an langId to an hinstance node
+//*****************************************************************************
+HRESOURCEDLL CCompRC::LookupNode(LocaleID langId, BOOL &fMissing)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pHash == NULL) return NULL;
+
+// Linear search
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (m_pHash[i].IsSet() && m_pHash[i].HasID(langId)) {
+ return m_pHash[i].GetLibraryHandle();
+ }
+ if (m_pHash[i].IsMissing() && m_pHash[i].HasID(langId))
+ {
+ fMissing = TRUE;
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+//*****************************************************************************
+// Add a new node to the map and return it.
+//*****************************************************************************
+const int MAP_STARTSIZE = 7;
+const int MAP_GROWSIZE = 5;
+
+HRESULT CCompRC::AddMapNode(LocaleID langId, HRESOURCEDLL hInst, BOOL fMissing)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+
+ if (m_pHash == NULL) {
+ m_pHash = new (nothrow)CCulturedHInstance[MAP_STARTSIZE];
+ if (m_pHash==NULL)
+ return E_OUTOFMEMORY;
+ m_nHashSize = MAP_STARTSIZE;
+ }
+
+// For now, place in first open slot
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (!m_pHash[i].IsSet() && !m_pHash[i].IsMissing()) {
+ if (fMissing)
+ {
+ m_pHash[i].SetMissing(langId);
+ }
+ else
+ {
+ m_pHash[i].Set(langId,hInst);
+ }
+
+ return S_OK;
+ }
+ }
+
+// Out of space, regrow
+ CCulturedHInstance * pNewHash = new (nothrow)CCulturedHInstance[m_nHashSize + MAP_GROWSIZE];
+ if (pNewHash)
+ {
+ memcpy(pNewHash, m_pHash, sizeof(CCulturedHInstance) * m_nHashSize);
+ delete [] m_pHash;
+ m_pHash = pNewHash;
+ if (fMissing)
+ {
+ m_pHash[m_nHashSize].SetMissing(langId);
+ }
+ else
+ {
+ m_pHash[m_nHashSize].Set(langId,hInst);
+ }
+ m_nHashSize += MAP_GROWSIZE;
+ }
+ else
+ return E_OUTOFMEMORY;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Initialize
+//*****************************************************************************
+LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.dll");
+
+HRESULT CCompRC::Init(LPCWSTR pResourceFile)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END;
+
+ // This function is called during Watson process. We need to make sure
+ // that this function is restartable.
+ //
+ // Make sure to NEVER null out the function callbacks in the Init
+ // function. They get set for the "Default CCompRC" during EEStartup
+ // and we want to make sure we don't wipe them out.
+
+ if (m_pResourceFile == NULL)
+ {
+ if(pResourceFile)
+ {
+ NewArrayHolder<WCHAR> pwszResourceFile(NULL);
+
+ DWORD lgth = (DWORD) wcslen(pResourceFile) + 1;
+ pwszResourceFile = new(nothrow) WCHAR[lgth];
+ if (pwszResourceFile)
+ {
+ wcscpy_s(pwszResourceFile, lgth, pResourceFile);
+ LPCWSTR pFile = pwszResourceFile.Extract();
+ if (InterlockedCompareExchangeT(&m_pResourceFile, pFile, NULL) != NULL)
+ {
+ delete [] pFile;
+ }
+ }
+ }
+ else
+ InterlockedCompareExchangeT(&m_pResourceFile, m_pDefaultResource, NULL);
+ }
+
+ if (m_pResourceFile == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (m_csMap == NULL)
+ {
+ // NOTE: there are times when the debugger's helper thread is asked to do a favor for another thread in the
+ // process. Typically, this favor involves putting up a dialog for the user. Putting up a dialog usually ends
+ // up involving the CCompRC code since (of course) the strings in the dialog are in a resource file. Thus, the
+ // debugger's helper thread will attempt to acquire this CRST. This is okay, since the helper thread only does
+ // these favors for other threads when there is no debugger attached. Thus, there are no deadlock hazards with
+ // this lock, and its safe for the helper thread to take, so this CRST is marked with CRST_DEBUGGER_THREAD.
+ CRITSEC_COOKIE csMap = ClrCreateCriticalSection(CrstCCompRC,
+ (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN));
+
+ if (csMap)
+ {
+ if (InterlockedCompareExchangeT(&m_csMap, csMap, NULL) != NULL)
+ {
+ ClrDeleteCriticalSection(csMap);
+ }
+ }
+ }
+
+ if (m_csMap == NULL)
+ return E_OUTOFMEMORY;
+
+ return S_OK;
+}
+
+void CCompRC::SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_fpGetThreadUICultureNames = fpGetThreadUICultureNames;
+ m_fpGetThreadUICultureId = fpGetThreadUICultureId;
+}
+
+void CCompRC::GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if(fpGetThreadUICultureNames)
+ *fpGetThreadUICultureNames=m_fpGetThreadUICultureNames;
+
+ if(fpGetThreadUICultureId)
+ *fpGetThreadUICultureId=m_fpGetThreadUICultureId;
+}
+
+void CCompRC::Destroy()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ // Free all resource libraries
+
+ //*****************************************************************************
+ // Free the loaded library if we ever loaded it and only if we are not on
+ // Win 95 which has a known bug with DLL unloading (it randomly unloads a
+ // dll on shut down, not necessarily the one you asked for). This is done
+ // only in debug mode to make coverage runs accurate.
+ //*****************************************************************************
+
+#if defined(_DEBUG)
+ if (m_Primary.GetLibraryHandle()) {
+ ::FreeLibrary(m_Primary.GetLibraryHandle());
+ }
+
+ if (m_pHash != NULL) {
+ int i;
+ for(i = 0; i < m_nHashSize; i ++) {
+ if (m_pHash[i].GetLibraryHandle() != NULL) {
+ ::FreeLibrary(m_pHash[i].GetLibraryHandle());
+ break;
+ }
+ }
+ }
+#endif
+
+ // destroy map structure
+ if(m_pResourceFile != m_pDefaultResource)
+ delete [] m_pResourceFile;
+ m_pResourceFile = NULL;
+
+ if(m_csMap) {
+ ClrDeleteCriticalSection(m_csMap);
+ ZeroMemory(&(m_csMap), sizeof(CRITSEC_COOKIE));
+ }
+
+ if(m_pHash != NULL) {
+ delete [] m_pHash;
+ m_pHash = NULL;
+ }
+}
+
+
+//*****************************************************************************
+// Initialization is done lazily, for backwards compatibility "mscorrc.dll"
+// is consider the default location for all strings that use CCompRC.
+// An instance value for CCompRC can be created to load resources from a different
+// resource dll.
+//*****************************************************************************
+LONG CCompRC::m_dwDefaultInitialized = 0;
+CCompRC CCompRC::m_DefaultResourceDll;
+
+CCompRC* CCompRC::GetDefaultResourceDll()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ if (m_dwDefaultInitialized)
+ return &m_DefaultResourceDll;
+
+ if(FAILED(m_DefaultResourceDll.Init(NULL)))
+ {
+ return NULL;
+ }
+ m_dwDefaultInitialized = 1;
+
+ return &m_DefaultResourceDll;
+}
+
+//*****************************************************************************
+//*****************************************************************************
+
+// String resouces packaged as PE files only exist on Windows
+#ifdef HOST_WINDOWS
+HRESULT CCompRC::GetLibrary(LocaleID langId, HRESOURCEDLL* phInst)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ PRECONDITION(phInst != NULL);
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+ HRESOURCEDLL hInst = 0;
+#ifndef DACCESS_COMPILE
+ HRESOURCEDLL hLibInst = 0; //Holds early library instance
+ BOOL fLibAlreadyOpen = FALSE; //Determine if we can close the opened library.
+#endif
+
+ // Try to match the primary entry, or else use the primary if we don't care.
+ if (m_Primary.IsSet())
+ {
+ if (langId == UICULTUREID_DONTCARE || m_Primary.HasID(langId))
+ {
+ hInst = m_Primary.GetLibraryHandle();
+ hr = S_OK;
+ }
+ }
+ else if(m_Primary.IsMissing())
+ {
+ // If primary is missing then the hash will not have anything either
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ }
+#ifndef DACCESS_COMPILE
+ // If this is the first visit, we must set the primary entry
+ else
+ {
+ // Don't immediately return if LoadLibrary fails so we can indicate the file was missing
+ hr = LoadLibrary(&hLibInst);
+ // If it's a transient failure, don't cache the failure
+ if (FAILED(hr) && Exception::IsTransient(hr))
+ {
+ return hr;
+ }
+
+ CRITSEC_Holder csh (m_csMap);
+ // As we expected
+ if (!m_Primary.IsSet() && !m_Primary.IsMissing())
+ {
+ hInst = hLibInst;
+ if (SUCCEEDED(hr))
+ {
+ m_Primary.Set(langId,hLibInst);
+ }
+ else
+ {
+ m_Primary.SetMissing(langId);
+ }
+ }
+
+ // Someone got into this critical section before us and set the primary already
+ else if (m_Primary.HasID(langId))
+ {
+ hInst = m_Primary.GetLibraryHandle();
+ fLibAlreadyOpen = TRUE;
+ }
+
+ // If neither case is true, someone got into this critical section before us and
+ // set the primary to other than the language we want...
+ else
+ {
+ fLibAlreadyOpen = TRUE;
+ }
+
+ IfFailRet(hr);
+
+ if (fLibAlreadyOpen)
+ {
+ FreeLibrary(hLibInst);
+ fLibAlreadyOpen = FALSE;
+ }
+ }
+#endif
+
+ // If we enter here, we know that the primary is set to something other than the
+ // language we want - multiple languages use the hash table
+ if (hInst == NULL && !m_Primary.IsMissing())
+ {
+ // See if the resource exists in the hash table
+ {
+ CRITSEC_Holder csh(m_csMap);
+ BOOL fMissing = FALSE;
+ hInst = LookupNode(langId, fMissing);
+ if (fMissing == TRUE)
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+ goto Exit;
+ }
+ }
+
+#ifndef DACCESS_COMPILE
+ // If we didn't find it, we have to load the library and insert it into the hash
+ if (hInst == NULL)
+ {
+ hr = LoadLibrary(&hLibInst);
+ // If it's a transient failure, don't cache the failure
+ if (FAILED(hr) && Exception::IsTransient(hr))
+ {
+ return hr;
+ }
+ {
+ CRITSEC_Holder csh (m_csMap);
+
+ // Double check - someone may have entered this section before us
+ BOOL fMissing = FALSE;
+ hInst = LookupNode(langId, fMissing);
+ if (hInst == NULL && !fMissing)
+ {
+ if (SUCCEEDED(hr))
+ {
+ hInst = hLibInst;
+ hr = AddMapNode(langId, hInst);
+ } else
+ {
+ HRESULT hrLoadLibrary = hr;
+ hr = AddMapNode(langId, hInst, TRUE /* fMissing */);
+ if (SUCCEEDED(hr))
+ {
+ hr = hrLoadLibrary;
+ }
+ }
+ }
+ else
+ {
+ fLibAlreadyOpen = TRUE;
+ }
+ }
+
+ if (fLibAlreadyOpen || FAILED(hr))
+ {
+ FreeLibrary(hLibInst);
+ }
+ }
+
+ // We found the node, so set hr to be a success.
+ else
+ {
+ hr = S_OK;
+ }
+#endif // DACCESS_COMPILE
+ }
+Exit:
+ *phInst = hInst;
+ return hr;
+}
+#endif // HOST_WINDOWS
+
+//*****************************************************************************
+// Load the string
+// We load the localized libraries and cache the handle for future use.
+// Mutliple threads may call this, so the cache structure is thread safe.
+//*****************************************************************************
+HRESULT CCompRC::LoadString(ResourceCategory eCategory, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed)
+{
+ WRAPPER_NO_CONTRACT;
+ LocaleIDValue langIdValue;
+ LocaleID langId;
+ // Must resolve current thread's langId to a dll.
+ if(m_fpGetThreadUICultureId) {
+ int ret = (*m_fpGetThreadUICultureId)(&langIdValue);
+
+ // Callback can't return 0, since that indicates empty.
+ // To indicate empty, callback should return UICULTUREID_DONTCARE
+ _ASSERTE(ret != 0);
+
+ if (ret == 0)
+ return E_UNEXPECTED;
+ langId=langIdValue;
+
+ }
+ else {
+ langId = UICULTUREID_DONTCARE;
+ }
+
+
+ return LoadString(eCategory, langId, iResourceID, szBuffer, iMax, pcwchUsed);
+}
+
+HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+#ifdef HOST_WINDOWS
+ HRESULT hr;
+ HRESOURCEDLL hInst = 0; //instance of cultured resource dll
+ int length;
+
+ hr = GetLibrary(langId, &hInst);
+
+ if (SUCCEEDED(hr))
+ {
+ // Now that we have the proper dll handle, load the string
+ _ASSERTE(hInst != NULL);
+
+ length = ::WszLoadString(hInst, iResourceID, szBuffer, iMax);
+ if(length > 0)
+ {
+ if(pcwchUsed)
+ {
+ *pcwchUsed = length;
+ }
+ return (S_OK);
+ }
+ if(GetLastError()==ERROR_SUCCESS)
+ hr=HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ else
+ hr=HRESULT_FROM_GetLastError();
+ }
+
+ // Return an empty string to save the people with a bad error handling
+ if (szBuffer && iMax)
+ *szBuffer = W('\0');
+
+ return hr;
+#else // HOST_WINDOWS
+ return LoadNativeStringResource(NATIVE_STRING_RESOURCE_TABLE(NATIVE_STRING_RESOURCE_NAME), iResourceID,
+ szBuffer, iMax, pcwchUsed);
+#endif // HOST_WINDOWS
+}
+
+#ifndef DACCESS_COMPILE
+
+// String resouces packaged as PE files only exist on Windows
+#ifdef HOST_WINDOWS
+HRESULT CCompRC::LoadResourceFile(HRESOURCEDLL * pHInst, LPCWSTR lpFileName)
+{
+ DWORD dwLoadLibraryFlags;
+ if(m_pResourceFile == m_pDefaultResource)
+ {
+ dwLoadLibraryFlags = LOAD_LIBRARY_AS_DATAFILE;
+ }
+ else
+ {
+ dwLoadLibraryFlags = 0;
+ }
+
+ if((*pHInst = WszLoadLibraryEx(lpFileName, NULL, dwLoadLibraryFlags)) == NULL)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ return S_OK;
+}
+
+//*****************************************************************************
+// Load the library for this thread's current language
+// Called once per language.
+// Search order is:
+// 1. Dll in localized path (<dir passed>\<lang name (en-US format)>\mscorrc.dll)
+// 2. Dll in localized (parent) path (<dir passed>\<lang name> (en format)\mscorrc.dll)
+// 3. Dll in root path (<dir passed>\mscorrc.dll)
+// 4. Dll in current path (<current dir>\mscorrc.dll)
+//*****************************************************************************
+HRESULT CCompRC::LoadLibraryHelper(HRESOURCEDLL *pHInst,
+ SString& rcPath)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = E_FAIL;
+
+
+ _ASSERTE(m_pResourceFile != NULL);
+
+ // must initialize before calling SString::Empty()
+ SString::Startup();
+
+ // Try and get both the culture fallback sequence
+
+ StringArrayList cultureNames;
+
+ if (m_fpGetThreadUICultureNames)
+ {
+ hr = (*m_fpGetThreadUICultureNames)(&cultureNames);
+ }
+ else
+ {
+ EX_TRY
+ {
+ cultureNames.Append(SString::Empty());
+ }
+ EX_CATCH_HRESULT(hr);
+ }
+
+ if (hr == E_OUTOFMEMORY)
+ return hr;
+ EX_TRY
+ {
+ for (DWORD i=0; i< cultureNames.GetCount();i++)
+ {
+ SString& sLang = cultureNames[i];
+
+ PathString rcPathName(rcPath);
+
+ if (!rcPathName.EndsWith(W("\\")))
+ {
+ rcPathName.Append(W("\\"));
+ }
+
+ if (!sLang.IsEmpty())
+ {
+ rcPathName.Append(sLang);
+ rcPathName.Append(W("\\"));
+ rcPathName.Append(m_pResourceFile);
+ }
+ else
+ {
+ rcPathName.Append(m_pResourceFile);
+ }
+
+ // Feedback for debugging to eliminate unecessary loads.
+ DEBUG_STMT(DbgWriteEx(W("Loading %s to load strings.\n"), rcPath.GetUnicode()));
+
+ // Load the resource library as a data file, so that the OS doesn't have
+ // to allocate it as code. This only works so long as the file contains
+ // only strings.
+ hr = LoadResourceFile(pHInst, rcPathName);
+ if (SUCCEEDED(hr))
+ {
+ break;
+ }
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ // Last ditch search effort in current directory
+ if (FAILED(hr))
+ {
+ hr = LoadResourceFile(pHInst, m_pResourceFile);
+ }
+
+ return hr;
+}
+
+// Two-stage approach:
+// First try module directory, then try CORSystemDirectory for default resource
+HRESULT CCompRC::LoadLibraryThrows(HRESOURCEDLL * pHInst)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pHInst != NULL);
+
+#ifdef CROSSGEN_COMPILE
+ // The resources are embeded into the .exe itself for crossgen
+ *pHInst = (HINSTANCE)GetClrModuleBase();
+#else
+
+#ifdef SELF_NO_HOST
+ _ASSERTE(!"CCompRC::LoadLibraryThrows not implemented for SELF_NO_HOST");
+ hr = E_NOTIMPL;
+#else // SELF_NO_HOST
+ PathString rcPath; // Path to resource DLL.
+
+ // Try first in the same directory as this dll.
+
+ hr = GetClrModuleDirectory(rcPath);
+ if (FAILED(hr))
+ return hr;
+
+ hr = LoadLibraryHelper(pHInst, rcPath);
+#endif
+
+#endif // CROSSGEN_COMPILE
+
+ return hr;
+}
+
+HRESULT CCompRC::LoadLibrary(HRESOURCEDLL * pHInst)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+#ifdef MODE_PREEMPTIVE
+ MODE_PREEMPTIVE;
+#endif
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ hr = LoadLibraryThrows(pHInst);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+#endif // DACCESS_COMPILE
+#endif //HOST_WINDOWS
diff --git a/src/coreclr/utilcode/check.cpp b/src/coreclr/utilcode/check.cpp
new file mode 100644
index 00000000000..4d7a0c74163
--- /dev/null
+++ b/src/coreclr/utilcode/check.cpp
@@ -0,0 +1,281 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//================================================================================
+// Assertion checking infrastructure
+//================================================================================
+
+#include "stdafx.h"
+#include <check.h>
+#include <sstring.h>
+#include <ex.h>
+#include <contract.h>
+
+#ifdef _DEBUG
+size_t CHECK::s_cLeakedBytes = 0;
+size_t CHECK::s_cNumFailures = 0;
+
+thread_local LONG CHECK::t_count;
+#endif
+
+BOOL CHECK::s_neverEnforceAsserts = 0;
+
+
+// Currently used for scan SPECIAL_HOLDER_* trickery
+DEBUG_NOINLINE BOOL CHECK::EnforceAssert_StaticCheckOnly()
+{
+ return s_neverEnforceAsserts;
+}
+
+#ifdef ENABLE_CONTRACTS_IMPL
+// Need a place to stick this, there is no contract.cpp...
+BOOL BaseContract::s_alwaysEnforceContracts = 1;
+
+#define SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask) \
+template<> void ContractViolationHolder<mask>::Enter() \
+{ \
+ SCAN_SCOPE_BEGIN; \
+ ANNOTATION_VIOLATION(mask); \
+ EnterInternal(mask); \
+};
+
+#define SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask) \
+template<> AutoCleanupContractViolationHolder<mask>::AutoCleanupContractViolationHolder(BOOL fEnterViolation) \
+{ \
+ SCAN_SCOPE_BEGIN; \
+ ANNOTATION_VIOLATION(mask); \
+ EnterInternal(fEnterViolation ? mask : 0); \
+};
+
+#define SPECIALIZED_VIOLATION(mask) \
+ SPECIALIZE_CONTRACT_VIOLATION_HOLDER(mask); \
+ SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER(mask)
+
+// There is a special case that requires 0... Why??? Who knows, let's fix that case.
+
+SPECIALIZED_VIOLATION(0);
+
+// Basic Specializations
+
+SPECIALIZED_VIOLATION(AllViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation);
+SPECIALIZED_VIOLATION(GCViolation);
+SPECIALIZED_VIOLATION(ModeViolation);
+SPECIALIZED_VIOLATION(FaultViolation);
+SPECIALIZED_VIOLATION(FaultNotFatal);
+SPECIALIZED_VIOLATION(HostViolation);
+SPECIALIZED_VIOLATION(TakesLockViolation);
+SPECIALIZED_VIOLATION(LoadsTypeViolation);
+
+// Other Specializations used by the RUNTIME, if you get a compile time error you need
+// to add the specific specialization that you are using here.
+
+SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|GCViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|ModeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|TakesLockViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|TakesLockViolation|LoadsTypeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|ModeViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation);
+SPECIALIZED_VIOLATION(GCViolation|FaultNotFatal|TakesLockViolation|ModeViolation);
+SPECIALIZED_VIOLATION(GCViolation|ModeViolation);
+SPECIALIZED_VIOLATION(FaultViolation|FaultNotFatal);
+SPECIALIZED_VIOLATION(FaultNotFatal|TakesLockViolation);
+
+
+
+#undef SPECIALIZED_VIOLATION
+#undef SPECIALIZE_AUTO_CLEANUP_CONTRACT_VIOLATION_HOLDER
+#undef SPECIALIZE_CONTRACT_VIOLATION_HOLDER
+
+#endif
+
+// Trigger triggers the actual check failure. The trigger may provide a reason
+// to include in the failure message.
+
+void CHECK::Trigger(LPCSTR reason)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ const char *messageString = NULL;
+ NewHolder<StackScratchBuffer> pScratch(NULL);
+ NewHolder<StackSString> pMessage(NULL);
+
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+
+ pScratch = new StackScratchBuffer();
+ pMessage = new StackSString();
+
+ pMessage->AppendASCII(reason);
+ pMessage->AppendASCII(": ");
+ if (m_message != NULL)
+ pMessage->AppendASCII((m_message != (LPCSTR)1) ? m_message : "<runtime check failure>");
+
+#if _DEBUG
+ pMessage->AppendASCII("FAILED: ");
+ pMessage->AppendASCII(m_condition);
+#endif
+
+ messageString = pMessage->GetANSI(*pScratch);
+ }
+ EX_CATCH
+ {
+ messageString = "<exception occurred while building failure description>";
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+#if _DEBUG
+ DbgAssertDialog((char*)m_file, m_line, (char *)messageString);
+#else
+ OutputDebugStringA(messageString);
+ DebugBreak();
+#endif
+
+}
+
+#ifdef _DEBUG
+// Setup records context info after a failure.
+
+void CHECK::Setup(LPCSTR message, LPCSTR condition, LPCSTR file, INT line)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ //
+ // It might be nice to collect all of the message here. But for now, we will just
+ // retain the innermost one.
+ //
+
+ if (m_message == NULL)
+ {
+ m_message = message;
+ m_condition = condition;
+ m_file = file;
+ m_line = line;
+ }
+
+#ifdef _DEBUG
+ else if (IsInAssert())
+ {
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+ // Try to build a stack of condition failures
+
+ StackSString context;
+ context.Printf("%s\n\t%s%s FAILED: %s\n\t\t%s, line: %d",
+ m_condition,
+ message && *message ? message : "",
+ message && *message ? ": " : "",
+ condition,
+ file, line);
+
+ m_condition = AllocateDynamicMessage(context);
+ }
+ EX_CATCH
+ {
+ // If anything goes wrong, we don't push extra context
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ }
+#endif
+
+#if defined(_DEBUG_IMPL)
+ if (IsInAssert() && IsDebuggerPresent())
+ {
+ DebugBreak();
+ }
+#endif
+}
+
+LPCSTR CHECK::FormatMessage(LPCSTR messageFormat, ...)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LPCSTR result = NULL;
+
+ // We never delete this allocated string. A dtor would conflict with places
+ // we use this around SEH stuff. We could have some fancy stack-based allocator,
+ // but that's too much work for now. In fact we believe that leaking is a reasonable
+ // policy, since allocations will only happen on a failed assert, and a failed assert
+ // will generally be fatal to the process.
+
+ // The most common place for format strings will be in an assert; in
+ // which case we don't care about leaking.
+ // But to be safe, if we're not-inside an assert, then we'll just use
+ // the format string literal to avoid allocated (and leaking) any memory.
+
+ CHECK chk;
+ if (!chk.IsInAssert())
+ result = messageFormat;
+ else
+ {
+ // This path is only run in debug. TakesLockViolation suppresses
+ // problems with SString below.
+ CONTRACT_VIOLATION(FaultNotFatal|TakesLockViolation);
+
+ EX_TRY
+ {
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+
+ // Format it.
+ va_list args;
+ va_start( args, messageFormat);
+
+ SString s;
+ s.VPrintf(messageFormat, args);
+
+ va_end(args);
+
+ result = AllocateDynamicMessage(s);
+
+ }
+ EX_CATCH
+ {
+ // If anything goes wrong, just use the format string.
+ result = messageFormat;
+ }
+ EX_END_CATCH(SwallowAllExceptions)
+ }
+
+ return result;
+}
+
+LPCSTR CHECK::AllocateDynamicMessage(const SString &s)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // Make a copy of it.
+ StackScratchBuffer buffer;
+ const char * pMsg = s.GetANSI(buffer);
+
+ // Must copy that into our own field.
+ size_t len = strlen(pMsg) + 1;
+ char * p = new char[len];
+ strcpy(p, pMsg);
+
+ // But we'll keep counters of how much we're leaking for diagnostic purposes.
+ s_cLeakedBytes += len;
+ s_cNumFailures ++;
+
+ // This should never fire.
+ // Note use an ASSERTE (not a check) to avoid a recursive deadlock.
+ _ASSERTE(s_cLeakedBytes < 10000 || !"Warning - check misuse - leaked over 10,000B due to unexpected usage pattern");
+ return p;
+}
+
+#endif
diff --git a/src/coreclr/utilcode/clrconfig.cpp b/src/coreclr/utilcode/clrconfig.cpp
new file mode 100644
index 00000000000..5142e395ac0
--- /dev/null
+++ b/src/coreclr/utilcode/clrconfig.cpp
@@ -0,0 +1,385 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// CLRConfig.cpp
+//
+
+//
+// Unified method of accessing configuration values from environment variables,
+// registry and config file. See file:../inc/CLRConfigValues.h for details on how to add config values.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "clrconfig.h"
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+//
+// Creating structs using the macro table in CLRConfigValues.h
+//
+
+// These macros intialize ConfigDWORDInfo structs.
+#define RETAIL_CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
+#define RETAIL_CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
+
+// These macros intialize ConfigStringInfo structs.
+#define RETAIL_CONFIG_STRING_INFO(symbol, name, description) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
+#define RETAIL_CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
+
+// TEMPORARY macros that intialize strings for config value accesses that haven't been moved over to
+// CLRConfig yet. Once all accesses have been moved, these macros (and corresponding instantiations in
+// file:../utilcode/CLRConfig.h) should be removed.
+#define RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+#define RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+//
+// Debug versions of the macros
+//
+#ifdef _DEBUG
+ #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, CLRConfig::EEConfig_default};
+ #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions) \
+ const CLRConfig::ConfigDWORDInfo CLRConfig::symbol = {name, defaultValue, lookupOptions};
+ #define CONFIG_STRING_INFO(symbol, name, description) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, CLRConfig::EEConfig_default};
+ #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions) \
+ const CLRConfig::ConfigStringInfo CLRConfig::symbol = {name, lookupOptions};
+ #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+ #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description) \
+ const LPCWSTR CLRConfig::symbol = name;
+#else
+ #define CONFIG_DWORD_INFO(symbol, name, defaultValue, description)
+ #define CONFIG_DWORD_INFO_EX(symbol, name, defaultValue, description, lookupOptions)
+ #define CONFIG_STRING_INFO(symbol, name, description)
+ #define CONFIG_STRING_INFO_EX(symbol, name, description, lookupOptions)
+ #define CONFIG_DWORD_INFO_DIRECT_ACCESS(symbol, name, description)
+ #define CONFIG_STRING_INFO_DIRECT_ACCESS(symbol, name, description)
+#endif // _DEBUG
+
+ // Now that we have defined what what the macros in file:../inc/CLRConfigValues.h mean, include it to generate the code.
+ #include "clrconfigvalues.h"
+
+#undef RETAIL_CONFIG_DWORD_INFO
+#undef RETAIL_CONFIG_STRING_INFO
+#undef RETAIL_CONFIG_DWORD_INFO_EX
+#undef RETAIL_CONFIG_STRING_INFO_EX
+#undef RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS
+#undef RETAIL_CONFIG_STRING_INFO_DIRECT_ACCESS
+#undef CONFIG_DWORD_INFO
+#undef CONFIG_STRING_INFO
+#undef CONFIG_DWORD_INFO_EX
+#undef CONFIG_STRING_INFO_EX
+#undef CONFIG_DWORD_INFO_DIRECT_ACCESS
+#undef CONFIG_STRING_INFO_DIRECT_ACCESS
+
+
+//
+// Look up a DWORD config value.
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details.
+//
+// * useDefaultIfNotSet - if true, fall back to the default value if the value is not set.
+//
+// * acceptExplicitDefaultFromRegutil - if false, only accept a value returned by REGUTIL if it is
+// different from the default value. This parameter is useful as a way to preserve existing
+// behavior.
+//
+// * result - the result.
+//
+// Return value:
+// * true for success, false otherwise.
+//
+// static
+DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info, bool acceptExplicitDefaultFromRegutil, /* [Out] */ bool *isDefault)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE (isDefault != nullptr);
+
+
+ //
+ // Set up REGUTIL options.
+ //
+ REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
+ BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
+
+ DWORD resultMaybe;
+ HRESULT hr = REGUTIL::GetConfigDWORD_DontUse_(info.name, info.defaultValue, &resultMaybe, level, prependCOMPlus);
+
+ if (!acceptExplicitDefaultFromRegutil)
+ {
+ // Ignore the default value even if it's set explicitly.
+ if (resultMaybe != info.defaultValue)
+ {
+ *isDefault = false;
+ return resultMaybe;
+ }
+ }
+ else
+ {
+ // If we are willing to accept the default value when it's set explicitly,
+ // checking the HRESULT here is sufficient. E_FAIL is returned when the
+ // default is used.
+ if (SUCCEEDED(hr))
+ {
+ *isDefault = false;
+ return resultMaybe;
+ }
+ }
+
+ *isDefault = true;
+ return info.defaultValue;
+}
+
+//
+// Look up a DWORD config value.
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+//
+// static
+DWORD CLRConfig::GetConfigValue(const ConfigDWORDInfo & info)
+{
+ // We pass false for 'acceptExplicitDefaultFromRegutil' to maintain the existing behavior of this function.
+ // Callers who don't need that behavior should switch to the other version of this function and pass true.
+ bool unused;
+ return GetConfigValue(info, false /* acceptExplicitDefaultFromRegutil */, &unused);
+}
+
+//
+// Look up a String config value.
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+//
+// Return value:
+// * Pointer to the string value, if found. You own the string that's returned. Returns NULL if the value
+// is not found.
+//
+// static
+LPWSTR CLRConfig::GetConfigValue(const ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ LPWSTR result = NULL;
+
+ // TODO: We swallow OOM exception here. Is this OK?
+ FAULT_NOT_FATAL();
+
+ // If this fails, result will stay NULL.
+ GetConfigValue(info, &result);
+
+ return result;
+}
+
+//
+// Look up a string config value, passing it out through a pointer reference.
+//
+// Return value:
+// * Reports out of memory errors (HRESULT E_OUTOFMEMORY).
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+// * outVal - Set to the result string. You own the string that's returned. Set to NULL if the value is
+// not found.
+//
+// static
+HRESULT CLRConfig::GetConfigValue(const ConfigStringInfo & info, __deref_out_z LPWSTR * outVal)
+{
+ CONTRACT(HRESULT) {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT (CONTRACT_RETURN E_OUTOFMEMORY);
+ POSTCONDITION(CheckPointer(outVal, NULL_OK)); // TODO: Should this check be *outVal instead of outVal?
+ } CONTRACT_END;
+
+ LPWSTR result = NULL;
+
+
+ //
+ // Set up REGUTIL options.
+ //
+ REGUTIL::CORConfigLevel level = GetConfigLevel(info.options);
+ BOOL prependCOMPlus = !CheckLookupOption(info, DontPrependCOMPlus_);
+
+ result = REGUTIL::GetConfigString_DontUse_(info.name, prependCOMPlus, level);
+
+ if ((result != NULL) && CheckLookupOption(info, TrimWhiteSpaceFromStringValue))
+ {
+ // If this fails, result remains untouched, so we'll just return the untrimmed
+ // value.
+ LPWSTR wszTrimmedResult = NULL;
+ if (SUCCEEDED(TrimWhiteSpace(result, &wszTrimmedResult)) &&
+ (wszTrimmedResult != NULL))
+ {
+ // wszTrimmedResult should be the result we return. Delete the untrimmed
+ // result.
+ delete [] result;
+ result = wszTrimmedResult;
+ }
+ }
+
+ *outVal = result;
+ RETURN S_OK;
+}
+
+//
+// Check whether an option is specified (e.g. explicitly listed) in any location
+//
+// Arguments:
+// * name - the name field of the desired ConfigDWORDInfo/ConfigStringInfo
+//
+// static
+BOOL CLRConfig::IsConfigOptionSpecified(LPCWSTR name)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Check REGUTIL, both with and without the COMPlus_ prefix
+ {
+ LPWSTR result = NULL;
+
+ result = REGUTIL::GetConfigString_DontUse_(name, TRUE);
+ if (result != NULL)
+ {
+ FreeConfigString(result);
+ return TRUE;
+ }
+
+ result = REGUTIL::GetConfigString_DontUse_(name, FALSE);
+ if (result != NULL)
+ {
+ FreeConfigString(result);
+ return TRUE;
+ }
+
+ }
+
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Given an input string, returns a newly-allocated string equal to the input but with
+// leading and trailing whitespace trimmed off. If input is already trimmed, or if
+// trimming would result in an empty string, this function sets the output string to NULL
+//
+// Caller must free *pwszTrimmed if non-NULL
+//
+// Arguments:
+// * wszOrig - String to trim
+// * pwszTrimmed - [out]: On return, points to newly allocated, trimmed string (or
+// NULL)
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+HRESULT CLRConfig::TrimWhiteSpace(LPCWSTR wszOrig, __deref_out_z LPWSTR * pwszTrimmed)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(wszOrig != NULL);
+ _ASSERTE(pwszTrimmed != NULL);
+
+ // In case we return early, set [out] to NULL by default
+ *pwszTrimmed = NULL;
+
+ // Get pointers into internal string that show where to do the trimming.
+ size_t cchOrig = wcslen(wszOrig);
+ if (!FitsIn<DWORD>(cchOrig))
+ return COR_E_OVERFLOW;
+ DWORD cchAfterTrim = (DWORD) cchOrig;
+ LPCWSTR wszAfterTrim = wszOrig;
+ ::TrimWhiteSpace(&wszAfterTrim, &cchAfterTrim);
+
+ // Is input string already trimmed? If so, save an allocation and just return.
+ if ((wszOrig == wszAfterTrim) && (cchOrig == cchAfterTrim))
+ {
+ // Yup, just return success
+ return S_OK;
+ }
+
+ if (cchAfterTrim == 0)
+ {
+ // After trimming, there's nothing left, so just return NULL
+ return S_OK;
+ }
+
+ // Create a new buffer to hold a copy of the trimmed string. Caller will be
+ // responsible for this buffer if we return it.
+ NewArrayHolder<WCHAR> wszTrimmedCopy(new (nothrow) WCHAR[cchAfterTrim + 1]);
+ if (wszTrimmedCopy == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ errno_t err = wcsncpy_s(wszTrimmedCopy, cchAfterTrim + 1, wszAfterTrim, cchAfterTrim);
+ if (err != 0)
+ {
+ return E_FAIL;
+ }
+
+ // Successfully made a copy of the trimmed string. Return it. Caller will be responsible for
+ // deleting it.
+ wszTrimmedCopy.SuppressRelease();
+ *pwszTrimmed = wszTrimmedCopy;
+ return S_OK;
+}
+
+
+//
+// Deallocation function for code:CLRConfig::FreeConfigString
+//
+void CLRConfig::FreeConfigString(__in_z LPWSTR str)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ delete [] str;
+}
+
+//
+// Helper method to translate LookupOptions to REGUTIL::CORConfigLevel.
+//
+//static
+REGUTIL::CORConfigLevel CLRConfig::GetConfigLevel(LookupOptions options)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ REGUTIL::CORConfigLevel level = (REGUTIL::CORConfigLevel) 0;
+
+ if(CheckLookupOption(options, IgnoreEnv) == FALSE)
+ level = static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_ENV);
+
+ return static_cast<REGUTIL::CORConfigLevel>(level | REGUTIL::COR_CONFIG_USER | REGUTIL::COR_CONFIG_MACHINE);
+}
diff --git a/src/coreclr/utilcode/clrhelpers.cpp b/src/coreclr/utilcode/clrhelpers.cpp
new file mode 100644
index 00000000000..98055909867
--- /dev/null
+++ b/src/coreclr/utilcode/clrhelpers.cpp
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// This is file to compile 2 public "headers" from ..\inc directory (we have to include stdafx.h first).
+//
+/***************************************************************************/
+
+#include "stdafx.h"
+
+#include "../inc/corhlpr.cpp"
+#include "../inc/corhlprpriv.cpp"
diff --git a/src/coreclr/utilcode/clrhost.cpp b/src/coreclr/utilcode/clrhost.cpp
new file mode 100644
index 00000000000..188418fccdf
--- /dev/null
+++ b/src/coreclr/utilcode/clrhost.cpp
@@ -0,0 +1,249 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+
+#include "stdafx.h"
+
+#include "clrhost.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "clrnt.h"
+#include "contract.h"
+
+#if HOST_WINDOWS
+extern "C" IMAGE_DOS_HEADER __ImageBase;
+#else
+static void* pImageBase = NULL;
+#endif
+
+void* GetClrModuleBase()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if HOST_WINDOWS
+ return (void*)&__ImageBase;
+#else // HOST_WINDOWS
+ // PAL_GetSymbolModuleBase defers to dladdr, which is typically a hash lookup through symbols.
+ // It should be fairly fast, however it may take a loader lock, so we will cache the result.
+ void* pRet = VolatileLoadWithoutBarrier(&pImageBase);
+ if (!pRet)
+ {
+ pImageBase = pRet = (void*)PAL_GetSymbolModuleBase((void*)GetClrModuleBase);
+ }
+
+ return pRet;
+#endif // HOST_WINDOWS
+}
+
+thread_local int t_CantAllocCount;
+
+#ifdef FAILPOINTS_ENABLED
+typedef int (*FHashStack) ();
+
+static FHashStack fHashStack = 0;
+static _TEB *HashStackSetupThread = NULL;
+static _TEB *RFSCustomDataSetupThread = NULL;
+
+static void SetupHashStack ()
+{
+ CANNOT_HAVE_CONTRACT;
+
+ FHashStack oldValue = InterlockedCompareExchangeT(&fHashStack,
+ reinterpret_cast<FHashStack>(1), reinterpret_cast<FHashStack>(0));
+ if ((size_t) oldValue >= 2) {
+ return;
+ }
+ else if ((size_t) oldValue == 0) {
+ // We are the first thread to initialize
+ HashStackSetupThread = NtCurrentTeb();
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HashStack) == 0) {
+ fHashStack = (FHashStack) 2;
+ return;
+ }
+
+ PAL_TRY(void *, unused, NULL) {
+ FHashStack func;
+ HMODULE hmod = LoadLibraryExA ("mscorrfs.dll", NULL, 0);
+ if (hmod) {
+ func = (FHashStack)GetProcAddress (hmod, "HashStack");
+ if (func == 0) {
+ func = (FHashStack)2;
+ }
+ }
+ else
+ func = (FHashStack)2;
+ fHashStack = func;
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ fHashStack = (FHashStack) 2;
+ }
+ PAL_ENDTRY;
+ }
+ else if (NtCurrentTeb() == HashStackSetupThread) {
+ // We get here while initializing
+ return;
+ }
+ else {
+ // All other threads will wait
+ while (fHashStack == (FHashStack) 1) {
+ ClrSleepEx (100, FALSE);
+ }
+ }
+}
+
+int RFS_HashStack ()
+{
+ CANNOT_HAVE_CONTRACT;
+
+ if ((size_t)fHashStack < 2) {
+ SetupHashStack ();
+ }
+
+ if ((size_t)fHashStack <= 2) {
+ return 0;
+ }
+ else
+ return fHashStack ();
+}
+
+#endif // FAILPOINTS_ENABLED
+
+DWORD GetClrModulePathName(SString& buffer)
+{
+#ifdef HOST_WINDOWS
+ return WszGetModuleFileName((HINSTANCE)GetClrModuleBase(), buffer);
+#else
+ return WszGetModuleFileName(PAL_GetPalHostModule(), buffer);
+#endif
+}
+
+#if defined(SELF_NO_HOST)
+
+HMODULE CLRLoadLibrary(LPCWSTR lpLibFileName)
+{
+ WRAPPER_NO_CONTRACT;
+ return CLRLoadLibraryEx(lpLibFileName, NULL, 0);
+}
+
+HMODULE CLRLoadLibraryEx(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
+{
+ WRAPPER_NO_CONTRACT;
+ return WszLoadLibraryEx(lpLibFileName, hFile, dwFlags);
+}
+
+BOOL CLRFreeLibrary(HMODULE hModule)
+{
+ WRAPPER_NO_CONTRACT;
+ return FreeLibrary(hModule);
+}
+
+#endif // defined(SELF_NO_HOST)
+
+
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+//-----------------------------------------------------------------------------------------------
+// Imposes a new typeload level limit for the scope of the holder. Any attempt to load a type
+// past that limit generates a contract violation assert.
+//
+// Do not invoke this directly. Invoke it through TRIGGERS_TYPE_LOAD or OVERRIDE_TYPE_LOAD_LEVEL_LIMIT.
+//
+// Arguments:
+// fConditional - if FALSE, this holder is a nop - supports the MAYBE_* macros.
+// newLevel - a value from classloadlevel.h - specifies the new max limit.
+// fEnforceLevelChangeDirection
+// - if true, implements TRIGGERS_TYPE_LOAD (level cap only allowed to decrease.)
+// if false, implements OVERRIDE (level allowed to increase - may only be used
+// by loader and only when recursion is structurally
+// impossible.)
+// szFunction,
+// szFile,
+// lineNum - records location of holder so we can print it in assertion boxes
+//
+// Assumptions:
+// ClrDebugState must have been set up (executing any contract will do this.)
+// Thread need *not* have a Thread* structure set up.
+//
+// Notes:
+// The holder withholds the assert if a LoadsTypeViolation suppress is in effect (but
+// still sets up the new limit.)
+//
+// As with other contract annotations, however, the violation suppression is *lifted*
+// within the scope guarded by the holder itself.
+//-----------------------------------------------------------------------------------------------
+LoadsTypeHolder::LoadsTypeHolder(BOOL fConditional,
+ UINT newLevel,
+ BOOL fEnforceLevelChangeDirection,
+ const char *szFunction,
+ const char *szFile,
+ int lineNum
+ )
+{
+ // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ m_fConditional = fConditional;
+ if (m_fConditional)
+ {
+ m_pClrDebugState = CheckClrDebugState();
+ _ASSERTE(m_pClrDebugState);
+
+ m_oldClrDebugState = *m_pClrDebugState;
+
+ if (fEnforceLevelChangeDirection)
+ {
+ if (newLevel > m_pClrDebugState->GetMaxLoadTypeLevel())
+ {
+ if (!( (LoadsTypeViolation|BadDebugState) & m_pClrDebugState->ViolationMask()))
+ {
+ CONTRACT_ASSERT("Illegal attempt to load a type beyond the current level limit.",
+ (m_pClrDebugState->GetMaxLoadTypeLevel() + 1) << Contract::LOADS_TYPE_Shift,
+ Contract::LOADS_TYPE_Mask,
+ szFunction,
+ szFile,
+ lineNum
+ );
+ }
+ }
+ }
+
+ m_pClrDebugState->ViolationMaskReset(LoadsTypeViolation);
+ m_pClrDebugState->SetMaxLoadTypeLevel(newLevel);
+
+ m_contractStackRecord.m_szFunction = szFunction;
+ m_contractStackRecord.m_szFile = szFile;
+ m_contractStackRecord.m_lineNum = lineNum;
+ m_contractStackRecord.m_testmask = (Contract::ALL_Disabled & ~((UINT)(Contract::LOADS_TYPE_Mask))) | (((newLevel) + 1) << Contract::LOADS_TYPE_Shift);
+ m_contractStackRecord.m_construct = fEnforceLevelChangeDirection ? "TRIGGERS_TYPE_LOAD" : "OVERRIDE_TYPE_LOAD_LEVEL_LIMIT";
+ m_contractStackRecord.m_pNext = m_pClrDebugState->GetContractStackTrace();
+ m_pClrDebugState->SetContractStackTrace(&m_contractStackRecord);
+
+
+ }
+} // LoadsTypeHolder::LoadsTypeHolder
+
+//-----------------------------------------------------------------------------------------------
+// Restores prior typeload level limit.
+//-----------------------------------------------------------------------------------------------
+LoadsTypeHolder::~LoadsTypeHolder()
+{
+ // This fcn makes non-scoped changes to ClrDebugState so we cannot use a runtime CONTRACT here.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ if (m_fConditional)
+ {
+ *m_pClrDebugState = m_oldClrDebugState;
+ }
+}
+
+#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
diff --git a/src/coreclr/utilcode/clrhost_nodependencies.cpp b/src/coreclr/utilcode/clrhost_nodependencies.cpp
new file mode 100644
index 00000000000..64185df41cb
--- /dev/null
+++ b/src/coreclr/utilcode/clrhost_nodependencies.cpp
@@ -0,0 +1,572 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+
+#include "stdafx.h"
+
+#include "clrhost.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "clrnt.h"
+#include "contract.h"
+
+#if defined __llvm__
+# if defined(__has_feature) && __has_feature(address_sanitizer)
+# define HAS_ADDRESS_SANITIZER
+# endif
+#endif
+
+#ifdef _DEBUG_IMPL
+
+//
+// I'd very much like for this to go away. Its used to disable all THROWS contracts within whatever DLL this
+// function is called from. That's obviously very, very bad, since there's no validation of those macros. But it
+// can be difficult to remove this without actually fixing every violation at the same time.
+//
+// When this flag is finally removed, remove RealCLRThrowsExceptionWorker() too and put CONTRACT_THROWS() in place
+// of it.
+//
+//
+static BOOL dbg_fDisableThrowCheck = FALSE;
+
+void DisableThrowCheck()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ dbg_fDisableThrowCheck = TRUE;
+}
+
+#ifdef HAS_ADDRESS_SANITIZER
+// use the functionality from address santizier (which does not throw exceptions)
+#else
+
+#define CLRThrowsExceptionWorker() RealCLRThrowsExceptionWorker(__FUNCTION__, __FILE__, __LINE__)
+
+static void RealCLRThrowsExceptionWorker(__in_z const char *szFunction,
+ __in_z const char *szFile,
+ int lineNum)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (dbg_fDisableThrowCheck)
+ {
+ return;
+ }
+
+ CONTRACT_THROWSEX(szFunction, szFile, lineNum);
+}
+
+#endif // HAS_ADDRESS_SANITIZER
+#endif //_DEBUG_IMPL
+
+#if defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+thread_local ClrDebugState* t_pClrDebugState;
+
+// Fls callback to deallocate ClrDebugState when our FLS block goes away.
+void FreeClrDebugState(LPVOID pTlsData)
+{
+ ClrDebugState *pClrDebugState = (ClrDebugState*)pTlsData;
+
+ // Make sure the ClrDebugState was initialized by a compatible version of
+ // utilcode.lib. If it was initialized by an older version, we just let it leak.
+ if (pClrDebugState && (pClrDebugState->ViolationMask() & CanFreeMe) && !(pClrDebugState->ViolationMask() & BadDebugState))
+ {
+ // Since "!(pClrDebugState->m_violationmask & BadDebugState)", we know we have
+ // a valid m_pLockData
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() != NULL);
+ HeapFree(GetProcessHeap(), 0, pClrDebugState->GetDbgStateLockData());
+
+ HeapFree(GetProcessHeap(), 0, pClrDebugState);
+ }
+}
+
+//=============================================================================================
+// Used to initialize the per-thread ClrDebugState. This is called once per thread (with
+// possible exceptions for OOM scenarios.)
+//
+// No matter what, this function will not return NULL. If it can't do its job because of OOM reasons,
+// it will return a pointer to &gBadClrDebugState which effectively disables contracts for
+// this thread.
+//=============================================================================================
+ClrDebugState *CLRInitDebugState()
+{
+ // This is our global "bad" debug state that thread use when they OOM on CLRInitDebugState.
+ // We really only need to initialize it once but initializing each time is convenient
+ // and has low perf impact.
+ static ClrDebugState gBadClrDebugState;
+ gBadClrDebugState.ViolationMaskSet( AllViolation );
+ gBadClrDebugState.SetOkToThrow();
+
+ ClrDebugState *pNewClrDebugState = NULL;
+ ClrDebugState *pClrDebugState = NULL;
+ DbgStateLockData *pNewLockData = NULL;
+
+ // Yuck. We cannot call the hosted allocator for ClrDebugState (it is impossible to maintain a guarantee
+ // that none of code paths, many of them called conditionally, don't themselves trigger a ClrDebugState creation.)
+ // We have to call the OS directly for this.
+ pNewClrDebugState = (ClrDebugState*)HeapAlloc(GetProcessHeap(), 0, sizeof(ClrDebugState));
+ if (pNewClrDebugState != NULL)
+ {
+ // Only allocate a DbgStateLockData if its owning ClrDebugState was successfully allocated
+ pNewLockData = (DbgStateLockData *)HeapAlloc(GetProcessHeap(), 0, sizeof(DbgStateLockData));
+ }
+
+ if ((pNewClrDebugState != NULL) && (pNewLockData != NULL))
+ {
+ // Both allocations succeeded, so initialize the structures, and have
+ // pNewClrDebugState point to pNewLockData. If either of the allocations
+ // failed, we'll use gBadClrDebugState for this thread, and free whichever of
+ // pNewClrDebugState or pNewLockData actually did get allocated (if either did).
+ // (See code in this function below, outside this block.)
+
+ pNewClrDebugState->SetStartingValues();
+ pNewClrDebugState->ViolationMaskSet( CanFreeMe );
+ _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState));
+
+ pNewLockData->SetStartingValues();
+ pNewClrDebugState->SetDbgStateLockData(pNewLockData);
+ }
+
+
+ // This is getting really diseased. All the one-time host init stuff inside the ClrFlsStuff could actually
+ // have caused mscorwks contracts to be executed since the last time we actually checked to see if the ClrDebugState
+ // needed creating.
+ //
+ // So we must make one last check to see if the ClrDebugState still needs creating.
+ //
+ ClrDebugState *pTmp = t_pClrDebugState;
+ if (pTmp != NULL)
+ {
+ // Recursive call set up ClrDebugState for us
+ pClrDebugState = pTmp;
+ }
+ else if ((pNewClrDebugState != NULL) && (pNewLockData != NULL))
+ {
+ // Normal case: our new ClrDebugState will be the one we just allocated.
+ // Note that we require BOTH the ClrDebugState and the DbgStateLockData
+ // structures to have been successfully allocated for contracts to be
+ // enabled for this thread.
+ _ASSERTE(!(pNewClrDebugState->ViolationMask() & BadDebugState));
+ _ASSERTE(pNewClrDebugState->GetDbgStateLockData() == pNewLockData);
+ pClrDebugState = pNewClrDebugState;
+ }
+ else
+ {
+ // OOM case: HeapAlloc of newClrDebugState failed.
+ pClrDebugState = &gBadClrDebugState;
+ }
+
+ _ASSERTE(pClrDebugState != NULL);
+
+ t_pClrDebugState = pClrDebugState;
+
+ // The ClrDebugState we allocated above made it into FLS iff
+ // the DbgStateLockData we allocated above made it into
+ // the FLS's ClrDebugState::m_pLockData
+ // These debug-only checks enforce this invariant
+
+ if (pClrDebugState != NULL)
+ {
+ // If we're here, then typically pClrDebugState is what's in FLS. However,
+ // it's possible that pClrDebugState is gBadClrDebugState, and FLS is NULL
+ // (if the last ClrFlsSetValue() failed). Either way, our checks below
+ // are valid ones to make.
+
+ if (pClrDebugState == pNewClrDebugState)
+ {
+ // ClrDebugState we allocated above made it into FLS, so DbgStateLockData
+ // must be there, too
+ _ASSERTE(pNewLockData != NULL);
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() == pNewLockData);
+ }
+ else
+ {
+ // ClrDebugState we allocated above did NOT make it into FLS,
+ // so the DbgStateLockData we allocated must not be there, either
+ _ASSERTE(pClrDebugState->GetDbgStateLockData() == NULL || pClrDebugState->GetDbgStateLockData() != pNewLockData);
+ }
+ }
+
+ // One more invariant: Because of ordering & conditions around the HeapAllocs above,
+ // we'll never have a DbgStateLockData without a ClrDebugState
+ _ASSERTE((pNewLockData == NULL) || (pNewClrDebugState != NULL));
+
+ if (pNewClrDebugState != NULL && pClrDebugState != pNewClrDebugState)
+ {
+ // We allocated a ClrDebugState which didn't make it into FLS, so free it.
+ HeapFree(GetProcessHeap(), 0, pNewClrDebugState);
+ if (pNewLockData != NULL)
+ {
+ // We also allocated a DbgStateLockData that didn't make it into FLS, so
+ // free it, too. (Remember, we asserted above that we can only have
+ // this unused DbgStateLockData if we had an unused ClrDebugState
+ // as well (which we just freed).)
+ HeapFree(GetProcessHeap(), 0, pNewLockData);
+ }
+ }
+
+ return pClrDebugState;
+} // CLRInitDebugState
+
+#endif //defined(_DEBUG_IMPL) && defined(ENABLE_CONTRACTS_IMPL)
+
+const NoThrow nothrow = { 0 };
+
+#ifdef HAS_ADDRESS_SANITIZER
+// use standard heap functions for address santizier
+#else
+
+#ifdef _DEBUG
+#ifdef TARGET_X86
+#define OS_HEAP_ALIGN 8
+#else
+#define OS_HEAP_ALIGN 16
+#endif
+#define CLRALLOC_TAG 0x2ce145f1
+#endif
+
+#ifdef HOST_WINDOWS
+static HANDLE g_hProcessHeap;
+#endif
+
+FORCEINLINE void* ClrMalloc(size_t size)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+#ifdef FAILPOINTS_ENABLED
+ if (RFS_HashStack())
+ return NULL;
+#endif
+
+ void* p;
+
+#ifdef _DEBUG
+ size += OS_HEAP_ALIGN;
+#endif
+
+#ifdef HOST_WINDOWS
+ HANDLE hHeap = g_hProcessHeap;
+ if (hHeap == NULL)
+ {
+ InterlockedCompareExchangeT(&g_hProcessHeap, ::GetProcessHeap(), NULL);
+ hHeap = g_hProcessHeap;
+ }
+
+ p = HeapAlloc(hHeap, 0, size);
+#else
+ p = malloc(size);
+#endif
+
+#ifdef _DEBUG
+ // Store the tag to detect heap contamination
+ if (p != NULL)
+ {
+ *((DWORD*)p) = CLRALLOC_TAG;
+ p = (BYTE*)p + OS_HEAP_ALIGN;
+ }
+#endif
+
+#ifndef SELF_NO_HOST
+ if (p == NULL
+ // If we have not created StressLog ring buffer, we should not try to use it.
+ // StressLog is going to do a memory allocation. We may enter an endless loop.
+ && StressLog::t_pCurrentThreadLog != NULL)
+ {
+ STRESS_LOG_OOM_STACK(size);
+ }
+#endif
+
+ return p;
+}
+
+FORCEINLINE void ClrFree(void* p)
+{
+ STATIC_CONTRACT_NOTHROW;
+
+#ifdef _DEBUG
+ if (p != NULL)
+ {
+ // Check the heap handle to detect heap contamination
+ p = (BYTE*)p - OS_HEAP_ALIGN;
+ if (*((DWORD*)p) != CLRALLOC_TAG)
+ _ASSERTE(!"Heap contamination detected! HeapFree was called on a heap other than the one that memory was allocated from.\n"
+ "Possible cause: you used new (executable) to allocate the memory, but didn't use DeleteExecutable() to free it.");
+ }
+#endif
+
+#ifdef HOST_WINDOWS
+ if (p != NULL)
+ HeapFree(g_hProcessHeap, 0, p);
+#else
+ free(p);
+#endif
+}
+
+void * __cdecl
+operator new(size_t n)
+{
+#ifdef _DEBUG_IMPL
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ void* result = ClrMalloc(n);
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl
+operator new[](size_t n)
+{
+#ifdef _DEBUG_IMPL
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ void* result = ClrMalloc(n);
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+};
+
+#endif // HAS_ADDRESS_SANITIZER
+
+void * __cdecl operator new(size_t n, const NoThrow&) NOEXCEPT
+{
+#ifdef HAS_ADDRESS_SANITIZER
+ // use standard heap functions for address santizier (which doesn't provide for NoThrow)
+ void * result = operator new(n);
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void* result = ClrMalloc(n);
+#endif // HAS_ADDRESS_SANITIZER
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const NoThrow&) NOEXCEPT
+{
+#ifdef HAS_ADDRESS_SANITIZER
+ // use standard heap functions for address santizier (which doesn't provide for NoThrow)
+ void * result = operator new[](n);
+#else
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ void* result = ClrMalloc(n);
+#endif // HAS_ADDRESS_SANITIZER
+ TRASH_LASTERROR;
+ return result;
+}
+
+#ifdef HAS_ADDRESS_SANITIZER
+// use standard heap functions for address santizier
+#else
+void __cdecl
+operator delete(void *p) NOEXCEPT
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ ClrFree(p);
+
+ TRASH_LASTERROR;
+}
+
+void __cdecl
+operator delete[](void *p) NOEXCEPT
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ ClrFree(p);
+ TRASH_LASTERROR;
+}
+
+#endif // HAS_ADDRESS_SANITIZER
+
+/* ------------------------------------------------------------------------ *
+ * New operator overloading for the executable heap
+ * ------------------------------------------------------------------------ */
+
+#ifdef HOST_WINDOWS
+
+HANDLE ClrGetProcessExecutableHeap()
+{
+ // Note: this can be called a little early for real contracts, so we use static contracts instead.
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ static HANDLE g_ExecutableHeapHandle;
+
+ //
+ // Create the executable heap lazily
+ //
+ if (g_ExecutableHeapHandle == NULL)
+ {
+
+ HANDLE ExecutableHeapHandle = HeapCreate(
+ HEAP_CREATE_ENABLE_EXECUTE, // heap allocation attributes
+ 0, // initial heap size
+ 0 // maximum heap size; 0 == growable
+ );
+
+ if (ExecutableHeapHandle == NULL)
+ return NULL;
+
+ HANDLE ExistingValue = InterlockedCompareExchangeT(&g_ExecutableHeapHandle, ExecutableHeapHandle, NULL);
+ if (ExistingValue != NULL)
+ {
+ HeapDestroy(ExecutableHeapHandle);
+ }
+ }
+
+ return g_ExecutableHeapHandle;
+}
+
+const CExecutable executable = { 0 };
+
+void * __cdecl operator new(size_t n, const CExecutable&)
+{
+#if defined(_DEBUG_IMPL)
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = HeapAlloc(hExecutableHeap, 0, n);
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const CExecutable&)
+{
+#if defined(_DEBUG_IMPL)
+ CLRThrowsExceptionWorker();
+#endif
+
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL) {
+ ThrowOutOfMemory();
+ }
+
+ void * result = HeapAlloc(hExecutableHeap, 0, n);
+ if (result == NULL) {
+ ThrowOutOfMemory();
+ }
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new(size_t n, const CExecutable&, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = HeapAlloc(hExecutableHeap, 0, n);
+ TRASH_LASTERROR;
+ return result;
+}
+
+void * __cdecl operator new[](size_t n, const CExecutable&, const NoThrow&)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ HANDLE hExecutableHeap = ClrGetProcessExecutableHeap();
+ if (hExecutableHeap == NULL)
+ return NULL;
+
+ void * result = HeapAlloc(hExecutableHeap, 0, n);
+ TRASH_LASTERROR;
+ return result;
+}
+
+#endif // HOST_WINDOWS
+
+#ifdef _DEBUG
+
+// This is a DEBUG routing to verify that a memory region complies with executable requirements
+BOOL DbgIsExecutable(LPVOID lpMem, SIZE_T length)
+{
+#if defined(CROSSGEN_COMPILE) || defined(TARGET_UNIX)
+ // No NX support on PAL or for crossgen compilations.
+ return TRUE;
+#else // !(CROSSGEN_COMPILE || TARGET_UNIX)
+ BYTE *regionStart = (BYTE*) ALIGN_DOWN((BYTE*)lpMem, GetOsPageSize());
+ BYTE *regionEnd = (BYTE*) ALIGN_UP((BYTE*)lpMem+length, GetOsPageSize());
+ _ASSERTE(length > 0);
+ _ASSERTE(regionStart < regionEnd);
+
+ while(regionStart < regionEnd)
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+
+ SIZE_T cbBytes = ClrVirtualQuery(regionStart, &mbi, sizeof(mbi));
+ _ASSERTE(cbBytes);
+
+ // The pages must have EXECUTE set
+ if(!(mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)))
+ return FALSE;
+
+ _ASSERTE((BYTE*)mbi.BaseAddress + mbi.RegionSize > regionStart);
+ regionStart = (BYTE*)mbi.BaseAddress + mbi.RegionSize;
+ }
+
+ return TRUE;
+#endif // CROSSGEN_COMPILE || TARGET_UNIX
+}
+
+#endif //_DEBUG
diff --git a/src/coreclr/utilcode/collections.cpp b/src/coreclr/utilcode/collections.cpp
new file mode 100644
index 00000000000..ed5271fde77
--- /dev/null
+++ b/src/coreclr/utilcode/collections.cpp
@@ -0,0 +1,970 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// Collections.cpp
+//
+
+//
+// This contains Collections C++ utility classes.
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+
+//
+//
+// CHashTable
+//
+//
+
+#ifndef DACCESS_COMPILE
+
+//*****************************************************************************
+// This is the second part of construction where we do all of the work that
+// can fail. We also take the array of structs here because the calling class
+// presumably needs to allocate it in its NewInit.
+//*****************************************************************************
+HRESULT CHashTable::NewInit( // Return status.
+ BYTE *pcEntries, // Array of structs we are managing.
+ ULONG iEntrySize) // Size of the entries.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iEntrySize >= sizeof(FREEHASHENTRY));
+
+ // Allocate the bucket chain array and init it.
+ if ((m_piBuckets = new (nothrow) ULONG [m_iBuckets]) == NULL)
+ return (OutOfMemory());
+ memset(m_piBuckets, 0xff, m_iBuckets * sizeof(ULONG));
+
+ // Save the array of structs we are managing.
+ m_pcEntries = (TADDR)pcEntries;
+ m_iEntrySize = iEntrySize;
+ return (S_OK);
+}
+
+//*****************************************************************************
+// Add the struct at the specified index in m_pcEntries to the hash chains.
+//*****************************************************************************
+BYTE *CHashTable::Add( // New entry.
+ ULONG iHash, // Hash value of entry to add.
+ ULONG iIndex) // Index of struct in m_pcEntries.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // The struct we are adding.
+
+ // Get a pointer to the entry we are adding.
+ psEntry = EntryPtr(iIndex);
+
+ // Compute the hash value for the entry.
+ iHash %= m_iBuckets;
+
+ _ASSERTE(m_piBuckets[iHash] != iIndex &&
+ (m_piBuckets[iHash] == UINT32_MAX || EntryPtr(m_piBuckets[iHash])->iPrev != iIndex));
+
+ // Setup this entry.
+ psEntry->iPrev = UINT32_MAX;
+ psEntry->iNext = m_piBuckets[iHash];
+
+ // Link it into the hash chain.
+ if (m_piBuckets[iHash] != UINT32_MAX)
+ EntryPtr(m_piBuckets[iHash])->iPrev = iIndex;
+ m_piBuckets[iHash] = iIndex;
+ return ((BYTE *) psEntry);
+}
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+void CHashTable::Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ ULONG iIndex) // Index of struct in m_pcEntries.
+{
+ WRAPPER_NO_CONTRACT;
+
+ HASHENTRY *psEntry; // Struct to delete.
+
+ // Get a pointer to the entry we are deleting.
+ psEntry = EntryPtr(iIndex);
+ Delete(iHash, psEntry);
+}
+
+//*****************************************************************************
+// Delete the struct at the specified index in m_pcEntries from the hash chains.
+//*****************************************************************************
+void CHashTable::Delete(
+ ULONG iHash, // Hash value of entry to delete.
+ HASHENTRY *psEntry) // The struct to delete.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Compute the hash value for the entry.
+ iHash %= m_iBuckets;
+
+ _ASSERTE(psEntry->iPrev != psEntry->iNext || psEntry->iPrev == UINT32_MAX);
+
+ // Fix the predecessor.
+ if (psEntry->iPrev == UINT32_MAX)
+ m_piBuckets[iHash] = psEntry->iNext;
+ else
+ EntryPtr(psEntry->iPrev)->iNext = psEntry->iNext;
+
+ // Fix the successor.
+ if (psEntry->iNext != UINT32_MAX)
+ EntryPtr(psEntry->iNext)->iPrev = psEntry->iPrev;
+}
+
+//*****************************************************************************
+// The item at the specified index has been moved, update the previous and
+// next item.
+//*****************************************************************************
+void CHashTable::Move(
+ ULONG iHash, // Hash value for the item.
+ ULONG iNew) // New location.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // The struct we are deleting.
+
+ psEntry = EntryPtr(iNew);
+ _ASSERTE(psEntry->iPrev != iNew && psEntry->iNext != iNew);
+
+ if (psEntry->iPrev != UINT32_MAX)
+ EntryPtr(psEntry->iPrev)->iNext = iNew;
+ else
+ m_piBuckets[iHash % m_iBuckets] = iNew;
+ if (psEntry->iNext != UINT32_MAX)
+ EntryPtr(psEntry->iNext)->iPrev = iNew;
+}
+
+#endif // !DACCESS_COMPILE
+
+//*****************************************************************************
+// Search the hash table for an entry with the specified key value.
+//*****************************************************************************
+BYTE *CHashTable::Find( // Index of struct in m_pcEntries.
+ ULONG iHash, // Hash value of the item.
+ SIZE_T key) // The key to match.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ ULONG iNext; // Used to traverse the chains.
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ // Start at the top of the chain.
+ iNext = m_piBuckets[iHash % m_iBuckets];
+
+ // Search until we hit the end.
+
+#ifdef _DEBUG
+ unsigned count = 0;
+#endif
+
+ while (iNext != UINT32_MAX)
+ {
+ // Compare the keys.
+ psEntry = EntryPtr(iNext);
+
+#ifdef _DEBUG
+ count++;
+#endif
+ if (!Cmp(key, psEntry))
+ {
+#ifdef _DEBUG
+ if (count > m_maxSearch)
+ m_maxSearch = count;
+#endif
+
+ return ((BYTE *) psEntry);
+ }
+
+ // Advance to the next item in the chain.
+ iNext = psEntry->iNext;
+ }
+
+ // We couldn't find it.
+ return (0);
+}
+
+//*****************************************************************************
+// Search the hash table for the next entry with the specified key value.
+//*****************************************************************************
+ULONG CHashTable::FindNext( // Index of struct in m_pcEntries.
+ SIZE_T key, // The key to match.
+ ULONG iIndex) // Index of previous match.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ ULONG iNext; // Used to traverse the chains.
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ // Start at the next entry in the chain.
+ iNext = EntryPtr(iIndex)->iNext;
+
+ // Search until we hit the end.
+ while (iNext != UINT32_MAX)
+ {
+ // Compare the keys.
+ psEntry = EntryPtr(iNext);
+ if (!Cmp(key, psEntry))
+ return (iNext);
+
+ // Advance to the next item in the chain.
+ iNext = psEntry->iNext;
+ }
+
+ // We couldn't find it.
+ return (UINT32_MAX);
+}
+
+//*****************************************************************************
+// Returns the next entry in the list.
+//*****************************************************************************
+BYTE *CHashTable::FindNextEntry( // The next entry, or0 for end of list.
+ HASHFIND *psSrch) // Search object.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ HASHENTRY *psEntry; // Used to traverse the chains.
+
+ for (;;)
+ {
+ // See if we already have one to use and if so, use it.
+ if (psSrch->iNext != UINT32_MAX)
+ {
+ psEntry = EntryPtr(psSrch->iNext);
+ psSrch->iNext = psEntry->iNext;
+ return ((BYTE *) psEntry);
+ }
+
+ // Advance to the next bucket.
+ if (psSrch->iBucket < m_iBuckets)
+ psSrch->iNext = m_piBuckets[psSrch->iBucket++];
+ else
+ break;
+ }
+
+ // There were no more entries to be found.
+ return (0);
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+CHashTable::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ ULONG numEntries)
+{
+ SUPPORTS_DAC;
+
+ // This class may be embedded so do not enum 'this'.
+ DacEnumMemoryRegion(m_pcEntries,
+ (ULONG)numEntries * m_iEntrySize);
+ DacEnumMemoryRegion(dac_cast<TADDR>(m_piBuckets),
+ (ULONG)m_iBuckets * sizeof(ULONG));
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+//
+//
+// CClosedHashBase
+//
+//
+
+//*****************************************************************************
+// Delete the given value. This will simply mark the entry as deleted (in
+// order to keep the collision chain intact). There is an optimization that
+// consecutive deleted entries leading up to a free entry are themselves freed
+// to reduce collisions later on.
+//*****************************************************************************
+void CClosedHashBase::Delete(
+ void *pData) // Key value to delete.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ BYTE *ptr;
+
+ // Find the item to delete.
+ if ((ptr = Find(pData)) == 0)
+ {
+ // You deleted something that wasn't there, why?
+ _ASSERTE(0);
+ return;
+ }
+
+ // For a perfect system, there are no collisions so it is free.
+ if (m_bPerfect)
+ {
+ SetStatus(ptr, FREE);
+
+ // One less non free entry.
+ --m_iCount;
+
+ return;
+ }
+
+ // Mark this entry deleted.
+ SetStatus(ptr, DELETED);
+
+ // If the next item is free, then we can go backwards freeing
+ // deleted entries which are no longer part of a chain. This isn't
+ // 100% great, but it will reduce collisions.
+ BYTE *pnext;
+ if ((pnext = ptr + m_iEntrySize) > EntryPtr(m_iSize - 1))
+ pnext = &m_rgData[0];
+ if (Status(pnext) != FREE)
+ return;
+
+ // We can now free consecutive entries starting with the one
+ // we just deleted, up to the first non-deleted one.
+ while (Status(ptr) == DELETED)
+ {
+ // Free this entry.
+ SetStatus(ptr, FREE);
+
+ // One less non free entry.
+ --m_iCount;
+
+ // Check the one before it, handle wrap around.
+ if ((ptr -= m_iEntrySize) < &m_rgData[0])
+ ptr = EntryPtr(m_iSize - 1);
+ }
+}
+
+
+//*****************************************************************************
+// Iterates over all active values, passing each one to pDeleteLoopFunc.
+// If pDeleteLoopFunc returns TRUE, the entry is deleted. This is safer
+// and faster than using FindNext() and Delete().
+//*****************************************************************************
+void CClosedHashBase::DeleteLoop(
+ DELETELOOPFUNC pDeleteLoopFunc, // Decides whether to delete item
+ void *pCustomizer) // Extra value passed to deletefunc.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ if (m_rgData == 0)
+ {
+ return;
+ }
+
+ for (i = 0; i < m_iSize; i++)
+ {
+ BYTE *pEntry = EntryPtr(i);
+ if (Status(pEntry) == USED)
+ {
+ if (pDeleteLoopFunc(pEntry, pCustomizer))
+ {
+ if (m_bPerfect)
+ {
+ SetStatus(pEntry, FREE);
+ // One less non free entry.
+ --m_iCount;
+ }
+ else
+ {
+ SetStatus(pEntry, DELETED);
+ }
+ }
+ }
+ }
+
+ if (!m_bPerfect)
+ {
+ // Now free DELETED entries that are no longer part of a chain.
+ for (i = 0; i < m_iSize; i++)
+ {
+ if (Status(EntryPtr(i)) == FREE)
+ {
+ break;
+ }
+ }
+ if (i != m_iSize)
+ {
+ int iFirstFree = i;
+
+ do
+ {
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ while (Status(EntryPtr(i)) == DELETED)
+ {
+ SetStatus(EntryPtr(i), FREE);
+
+
+ // One less non free entry.
+ --m_iCount;
+
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ }
+
+ while (Status(EntryPtr(i)) != FREE)
+ {
+ if (i-- == 0)
+ {
+ i = m_iSize - 1;
+ }
+ }
+
+ }
+ while (i != iFirstFree);
+ }
+ }
+
+}
+
+//*****************************************************************************
+// Lookup a key value and return a pointer to the element if found.
+//*****************************************************************************
+BYTE *CClosedHashBase::Find( // The item if found, 0 if not.
+ void *pData) // The key to lookup.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // Safety check.
+ if (!m_rgData || m_iCount == 0)
+ return (0);
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // For a perfect table, the bucket is the correct one.
+ if (m_bPerfect)
+ {
+ // If the value is there, it is the correct one.
+ if (Status(EntryPtr(iBucket)) != FREE)
+ return (EntryPtr(iBucket));
+ return (0);
+ }
+
+ // Walk the bucket list looking for the item.
+ for (i=iBucket; Status(EntryPtr(i)) != FREE; )
+ {
+ // Don't look at deleted items.
+ if (Status(EntryPtr(i)) == DELETED)
+ {
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ continue;
+ }
+
+ // Check this one.
+ if (Compare(pData, EntryPtr(i)) == 0)
+ return (EntryPtr(i));
+
+ // If we never collided while adding items, then there is
+ // no point in scanning any further.
+ if (!m_iCollisions)
+ return (0);
+
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ }
+ return (0);
+}
+
+
+
+//*****************************************************************************
+// Look for an item in the table. If it isn't found, then create a new one and
+// return that.
+//*****************************************************************************
+BYTE *CClosedHashBase::FindOrAdd( // The item if found, 0 if not.
+ void *pData, // The key to lookup.
+ bool &bNew) // true if created.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // If we haven't allocated any memory, or it is too small, fix it.
+ if (!m_rgData || ((m_iCount + 1) > (m_iSize * 3 / 4) && !m_bPerfect))
+ {
+ if (!ReHash())
+ return (0);
+ }
+
+ // Assume we find it.
+ bNew = false;
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % m_iBuckets;
+
+ // For a perfect table, the bucket is the correct one.
+ if (m_bPerfect)
+ {
+ // If the value is there, it is the correct one.
+ if (Status(EntryPtr(iBucket)) != FREE)
+ return (EntryPtr(iBucket));
+ i = iBucket;
+ }
+ else
+ {
+ // Walk the bucket list looking for the item.
+ for (i=iBucket; Status(EntryPtr(i)) != FREE; )
+ {
+ // Don't look at deleted items.
+ if (Status(EntryPtr(i)) == DELETED)
+ {
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ continue;
+ }
+
+ // Check this one.
+ if (Compare(pData, EntryPtr(i)) == 0)
+ return (EntryPtr(i));
+
+ // One more to count.
+ ++m_iCollisions;
+
+ // Handle wrap around.
+ if (++i >= m_iSize)
+ i = 0;
+ }
+ }
+
+ // We've found an open slot, use it.
+ _ASSERTE(Status(EntryPtr(i)) == FREE);
+ bNew = true;
+ ++m_iCount;
+ return (EntryPtr(i));
+}
+
+//*****************************************************************************
+// This helper actually does the add for you.
+//*****************************************************************************
+BYTE *CClosedHashBase::DoAdd(void *pData, BYTE *rgData, int &iBuckets, int iSize,
+ int &iCollisions, int &iCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ unsigned int iHash; // Hash value for this data.
+ int iBucket; // Which bucke to start at.
+ int i; // Loop control.
+
+ // Hash to the bucket.
+ iHash = Hash(pData);
+ iBucket = iHash % iBuckets;
+
+ // For a perfect table, the bucket is free.
+ if (m_bPerfect)
+ {
+ i = iBucket;
+ _ASSERTE(Status(EntryPtr(i, rgData)) == FREE);
+ }
+ // Need to scan.
+ else
+ {
+ // Walk the bucket list looking for a slot.
+ for (i=iBucket; Status(EntryPtr(i, rgData)) != FREE; )
+ {
+ // Handle wrap around.
+ if (++i >= iSize)
+ i = 0;
+
+ // If we made it this far, we collided.
+ ++iCollisions;
+ }
+ }
+
+ // One more item in list.
+ ++iCount;
+
+ // Return the new slot for the caller.
+ return (EntryPtr(i, rgData));
+}
+
+//*****************************************************************************
+// This function is called either to init the table in the first place, or
+// to rehash the table if we ran out of room.
+//*****************************************************************************
+bool CClosedHashBase::ReHash() // true if successful.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Allocate memory if we don't have any.
+ if (!m_rgData)
+ {
+ if ((m_rgData = new (nothrow) BYTE [m_iSize * m_iEntrySize]) == 0)
+ return (false);
+ InitFree(&m_rgData[0], m_iSize);
+ return (true);
+ }
+
+ // We have entries already, allocate a new table.
+ BYTE *rgTemp, *p;
+ int iBuckets = m_iBuckets * 2 - 1;
+ int iSize = iBuckets + 7;
+ int iCollisions = 0;
+ int iCount = 0;
+
+ if ((rgTemp = new (nothrow) BYTE [iSize * m_iEntrySize]) == 0)
+ return (false);
+ InitFree(&rgTemp[0], iSize);
+ m_bPerfect = false;
+
+ // Rehash the data.
+ for (int i=0; i<m_iSize; i++)
+ {
+ // Only copy used entries.
+ if (Status(EntryPtr(i)) != USED)
+ continue;
+
+ // Add this entry to the list again.
+ VERIFY((p = DoAdd(GetKey(EntryPtr(i)), rgTemp, iBuckets,
+ iSize, iCollisions, iCount)));
+ memmove(p, EntryPtr(i), m_iEntrySize);
+ }
+
+ // Reset internals.
+ delete [] m_rgData;
+ m_rgData = rgTemp;
+ m_iBuckets = iBuckets;
+ m_iSize = iSize;
+ m_iCollisions = iCollisions;
+ m_iCount = iCount;
+ return (true);
+}
+
+
+//
+//
+// CStructArray
+//
+//
+
+
+//*****************************************************************************
+// Returns a pointer to the (iIndex)th element of the array, shifts the elements
+// in the array if the location is already full. The iIndex cannot exceed the count
+// of elements in the array.
+//*****************************************************************************
+void *CStructArray::InsertThrowing(
+ int iIndex)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iIndex >= 0);
+
+ // We can not insert an element further than the end of the array.
+ if (iIndex > m_iCount)
+ return (NULL);
+
+ // The array should grow, if we can't fit one more element into the array.
+ Grow(1);
+
+ // The pointer to be returned.
+ BYTE *pcList = m_pList + iIndex * m_iElemSize;
+
+ // See if we need to slide anything down.
+ if (iIndex < m_iCount)
+ memmove(pcList + m_iElemSize, pcList, (m_iCount - iIndex) * m_iElemSize);
+ ++m_iCount;
+ return(pcList);
+}
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+void *CStructArray::Insert(int iIndex)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ void *result = NULL;
+ EX_TRY
+ {
+ result = InsertThrowing(iIndex);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Allocate a new element at the end of the dynamic array and return a pointer
+// to it.
+//*****************************************************************************
+void *CStructArray::AppendThrowing()
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ // The array should grow, if we can't fit one more element into the array.
+ Grow(1);
+
+ return (m_pList + m_iCount++ * m_iElemSize);
+}
+
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+void *CStructArray::Append()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ void *result = NULL;
+ EX_TRY
+ {
+ result = AppendThrowing();
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Allocate enough memory to have at least iCount items. This is a one shot
+// check for a block of items, instead of requiring singleton inserts. It also
+// avoids realloc headaches caused by growth, since you can do the whole block
+// in one piece of code. If the array is already large enough, this is a no-op.
+//*****************************************************************************
+void CStructArray::AllocateBlockThrowing(int iCount)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ if (m_iSize < m_iCount+iCount)
+ Grow(iCount);
+ m_iCount += iCount;
+}
+
+//*****************************************************************************
+// Non-throwing variant
+//*****************************************************************************
+int CStructArray::AllocateBlock(int iCount)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int result = FALSE;
+ EX_TRY
+ {
+ AllocateBlockThrowing(iCount);
+ result = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+
+//*****************************************************************************
+// Deletes the specified element from the array.
+//*****************************************************************************
+void CStructArray::Delete(
+ int iIndex)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(iIndex >= 0);
+
+ // See if we need to slide anything down.
+ if (iIndex < --m_iCount)
+ {
+ BYTE *pcList = m_pList + iIndex * m_iElemSize;
+ memmove(pcList, pcList + m_iElemSize, (m_iCount - iIndex) * m_iElemSize);
+ }
+}
+
+
+//*****************************************************************************
+// Grow the array if it is not possible to fit iCount number of new elements.
+//*****************************************************************************
+void CStructArray::Grow(
+ int iCount)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ BYTE *pTemp; // temporary pointer used in realloc.
+ int iGrow;
+
+ if (m_iSize < m_iCount+iCount)
+ {
+ if (m_pList == NULL)
+ {
+ iGrow = max(m_iGrowInc, iCount);
+
+ //<TODO>@todo this is a workaround because I don't want to do resize right now.</TODO>
+ //check added to make PREFAST happy
+ S_SIZE_T newSize = S_SIZE_T(iGrow) * S_SIZE_T(m_iElemSize);
+ if(newSize.IsOverflow())
+ ThrowOutOfMemory();
+ else
+ {
+ m_pList = new BYTE[newSize.Value()];
+ m_iSize = iGrow;
+ m_bFree = true;
+ }
+ }
+ else
+ {
+ // Adjust grow size as a ratio to avoid too many reallocs.
+ if (m_iSize / m_iGrowInc >= 3)
+ { // Don't overflow and go negative.
+ int newinc = m_iGrowInc * 2;
+ if (newinc > m_iGrowInc)
+ m_iGrowInc = newinc;
+ }
+
+ iGrow = max(m_iGrowInc, iCount);
+
+ // try to allocate memory for reallocation.
+ S_SIZE_T allocSize = (S_SIZE_T(m_iSize) + S_SIZE_T(iGrow)) * S_SIZE_T(m_iElemSize);
+ S_SIZE_T copyBytes = S_SIZE_T(m_iSize) * S_SIZE_T(m_iElemSize);
+ if(allocSize.IsOverflow() || copyBytes.IsOverflow())
+ ThrowOutOfMemory();
+ if (m_bFree)
+ { // We already own memory.
+ pTemp = new BYTE[allocSize.Value()];
+ memcpy (pTemp, m_pList, copyBytes.Value());
+ delete [] m_pList;
+ }
+ else
+ { // We don't own memory; get our own.
+ pTemp = new BYTE[allocSize.Value()];
+ memcpy(pTemp, m_pList, copyBytes.Value());
+ m_bFree = true;
+ }
+ m_pList = pTemp;
+ m_iSize += iGrow;
+ }
+ }
+}
+
+
+//*****************************************************************************
+// Free the memory for this item.
+//*****************************************************************************
+void CStructArray::Clear()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Free the chunk of memory.
+ if (m_bFree && m_pList != NULL)
+ delete [] m_pList;
+
+ m_pList = NULL;
+ m_iSize = 0;
+ m_iCount = 0;
+}
diff --git a/src/coreclr/utilcode/comex.cpp b/src/coreclr/utilcode/comex.cpp
new file mode 100644
index 00000000000..3a564553d22
--- /dev/null
+++ b/src/coreclr/utilcode/comex.cpp
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// COMEx.cpp
+//
+
+//
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "string.h"
+#include "ex.h"
+#include "holder.h"
+#include "corerror.h"
+
+// ---------------------------------------------------------------------------
+// COMException class. Implements exception API for standard COM-based error info
+// ---------------------------------------------------------------------------
+
+COMException::~COMException()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_pErrorInfo != NULL)
+ m_pErrorInfo->Release();
+}
+
+IErrorInfo *COMException::GetErrorInfo()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ IErrorInfo *pErrorInfo = m_pErrorInfo;
+ if (pErrorInfo != NULL)
+ pErrorInfo->AddRef();
+ return pErrorInfo;
+}
+
+void COMException::GetMessage(SString &string)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (m_pErrorInfo != NULL)
+ {
+ BSTRHolder message(NULL);
+ if (SUCCEEDED(m_pErrorInfo->GetDescription(&message)))
+ string.Set(message, SysStringLen(message));
+ }
+}
+
diff --git a/src/coreclr/utilcode/configuration.cpp b/src/coreclr/utilcode/configuration.cpp
new file mode 100644
index 00000000000..e67507d5bae
--- /dev/null
+++ b/src/coreclr/utilcode/configuration.cpp
@@ -0,0 +1,135 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// --------------------------------------------------------------------------------------------------
+// configuration.cpp
+//
+//
+// Access and update configuration values, falling back on legacy CLRConfig methods where necessary.
+//
+// --------------------------------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "clrconfig.h"
+#include "configuration.h"
+
+LPCWSTR *knobNames = nullptr;
+LPCWSTR *knobValues = nullptr;
+int numberOfKnobs = 0;
+
+void Configuration::InitializeConfigurationKnobs(int numberOfConfigs, LPCWSTR *names, LPCWSTR *values)
+{
+ numberOfKnobs = numberOfConfigs;
+
+ // Neither should be null, or both should be null
+ _ASSERT(!((names == nullptr) ^ (values == nullptr)));
+
+ knobNames = names;
+ knobValues = values;
+}
+
+static LPCWSTR GetConfigurationValue(LPCWSTR name)
+{
+ _ASSERT(name != nullptr);
+ if (name == nullptr || knobNames == nullptr || knobValues == nullptr)
+ {
+ return nullptr;
+ }
+
+ for (int i = 0; i < numberOfKnobs; ++i)
+ {
+ _ASSERT(knobNames[i] != nullptr);
+ if (wcscmp(name, knobNames[i]) == 0)
+ {
+ return knobValues[i];
+ }
+ }
+
+ return nullptr;
+}
+
+DWORD Configuration::GetKnobDWORDValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo)
+{
+ bool returnedDefaultValue;
+ DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue);
+ if (!returnedDefaultValue)
+ {
+ return legacyValue;
+ }
+
+ LPCWSTR knobValue = GetConfigurationValue(name);
+ if (knobValue != nullptr)
+ {
+ return wcstoul(knobValue, nullptr, 0);
+ }
+
+ return legacyValue;
+}
+
+DWORD Configuration::GetKnobDWORDValue(LPCWSTR name, DWORD defaultValue)
+{
+ LPCWSTR knobValue = GetConfigurationValue(name);
+ if (knobValue != nullptr)
+ {
+ return wcstoul(knobValue, nullptr, 0);
+ }
+
+ return defaultValue;
+}
+
+ULONGLONG Configuration::GetKnobULONGLONGValue(LPCWSTR name, ULONGLONG defaultValue)
+{
+ LPCWSTR knobValue = GetConfigurationValue(name);
+ if (knobValue != nullptr)
+ {
+ return _wcstoui64(knobValue, nullptr, 0);
+ }
+
+ return defaultValue;
+}
+
+LPCWSTR Configuration::GetKnobStringValue(LPCWSTR name, const CLRConfig::ConfigStringInfo& stringInfo)
+{
+ LPCWSTR value = CLRConfig::GetConfigValue(stringInfo);
+ if (value == nullptr)
+ {
+ value = GetConfigurationValue(name);
+ }
+
+ return value;
+}
+
+LPCWSTR Configuration::GetKnobStringValue(LPCWSTR name)
+{
+ return GetConfigurationValue(name);
+}
+
+bool Configuration::GetKnobBooleanValue(LPCWSTR name, const CLRConfig::ConfigDWORDInfo& dwordInfo)
+{
+ bool returnedDefaultValue;
+ DWORD legacyValue = CLRConfig::GetConfigValue(dwordInfo, true /* acceptExplicitDefaultFromRegutil */, &returnedDefaultValue);
+ if (!returnedDefaultValue)
+ {
+ return (legacyValue != 0);
+ }
+
+ LPCWSTR knobValue = GetConfigurationValue(name);
+ if (knobValue != nullptr)
+ {
+ return (wcscmp(knobValue, W("true")) == 0);
+ }
+
+ return (legacyValue != 0);
+}
+
+bool Configuration::GetKnobBooleanValue(LPCWSTR name, bool defaultValue)
+{
+ LPCWSTR knobValue = GetConfigurationValue(name);
+ if (knobValue != nullptr)
+ {
+ return (wcscmp(knobValue, W("true")) == 0);
+ }
+
+ return defaultValue;
+}
diff --git a/src/coreclr/utilcode/corimage.cpp b/src/coreclr/utilcode/corimage.cpp
new file mode 100644
index 00000000000..a1aff8fe82d
--- /dev/null
+++ b/src/coreclr/utilcode/corimage.cpp
@@ -0,0 +1,259 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+
+/*============================================================
+**
+** CorImage.cpp
+**
+** IMAGEHLP routines so we can avoid early binding to that DLL.
+**
+===========================================================*/
+
+#include "stdafx.h"
+#include "contract.h"
+#include <daccess.h>
+#include "corimage.h"
+#include "safemath.h"
+
+#define RTL_MEG (1024UL * 1024UL)
+#define RTLP_IMAGE_MAX_DOS_HEADER ( 256UL * RTL_MEG)
+
+// IMAGE_FIRST_SECTION doesn't need 32/64 versions since the file header is
+// the same either way.
+
+#define PTR_IMAGE_FIRST_SECTION( ntheader ) \
+ PTR_IMAGE_SECTION_HEADER \
+ (dac_cast<TADDR>(ntheader) + \
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader ) + \
+ VAL16((ntheader)->FileHeader.SizeOfOptionalHeader) \
+ )
+
+#ifndef DACCESS_COMPILE
+
+IMAGE_NT_HEADERS *Cor_RtlImageNtHeader(VOID *pvBase, ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ IMAGE_NT_HEADERS *pNtHeaders = NULL;
+ if (pvBase && (pvBase != (VOID*)-1)) {
+ struct Param
+ {
+ IMAGE_DOS_HEADER *pDos;
+ ULONG FileLength;
+ IMAGE_NT_HEADERS *pNtHeaders;
+ } param;
+ param.pDos = (IMAGE_DOS_HEADER*)pvBase;
+ param.FileLength = FileLength;
+ param.pNtHeaders = pNtHeaders;
+
+ PAL_TRY(Param *, pParam, &param) {
+ if ( (pParam->pDos->e_magic == VAL16(IMAGE_DOS_SIGNATURE))
+ && ((DWORD)VAL32(pParam->pDos->e_lfanew) < RTLP_IMAGE_MAX_DOS_HEADER)
+ && ovadd_lt((DWORD)VAL32(pParam->pDos->e_lfanew), sizeof(IMAGE_FILE_HEADER) + sizeof(DWORD), pParam->FileLength)) {
+ pParam->pNtHeaders = (IMAGE_NT_HEADERS*)((BYTE*)pParam->pDos + VAL32(pParam->pDos->e_lfanew));
+ if (pParam->pNtHeaders->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ pParam->pNtHeaders = NULL;
+ }
+ }
+ PAL_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
+ param.pNtHeaders = NULL;
+ }
+ PAL_ENDTRY
+
+ pNtHeaders = param.pNtHeaders;
+ }
+ return pNtHeaders;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection32(PTR_IMAGE_NT_HEADERS32 NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<NtHeaders->FileHeader.NumberOfSections; i++) {
+ if (FileLength &&
+ (((VAL32(NtSection->PointerToRawData) > FileLength)) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection64(PTR_IMAGE_NT_HEADERS64 NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) {
+ if (FileLength &&
+ (((VAL32(NtSection->PointerToRawData) > FileLength)) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva < VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaToSection(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToSection32((PTR_IMAGE_NT_HEADERS32)NtHeaders,
+ Rva, FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToSection64((PTR_IMAGE_NT_HEADERS64)NtHeaders,
+ Rva, FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa32(PTR_IMAGE_NT_HEADERS32 NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection32(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection != NULL)
+ return (Base +
+ (Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa64(PTR_IMAGE_NT_HEADERS64 NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection64(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection != NULL)
+ return (Base +
+ (Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
+
+EXTERN_C PBYTE Cor_RtlImageRvaToVa(PTR_IMAGE_NT_HEADERS NtHeaders,
+ PBYTE Base,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders,
+ Base, Rva, FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders,
+ Base, Rva, FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PBYTE Cor_RtlImageDirToVa(PTR_IMAGE_NT_HEADERS NtHeaders,
+ PBYTE Base,
+ UINT DirIndex,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ if (NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ return Cor_RtlImageRvaToVa32((PTR_IMAGE_NT_HEADERS32)NtHeaders, Base,
+ VAL32(((PTR_IMAGE_NT_HEADERS32)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress),
+ FileLength);
+ else if(NtHeaders->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ return Cor_RtlImageRvaToVa64((PTR_IMAGE_NT_HEADERS64)NtHeaders, Base,
+ VAL32(((PTR_IMAGE_NT_HEADERS64)NtHeaders)->OptionalHeader.DataDirectory[DirIndex].VirtualAddress),
+ FileLength);
+ else {
+ _ASSERTE(!"Invalid File Type");
+ return NULL;
+ }
+}
+
+EXTERN_C PIMAGE_SECTION_HEADER
+Cor_RtlImageRvaRangeToSection(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG Range,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ ULONG i;
+ PTR_IMAGE_SECTION_HEADER NtSection;
+
+ if (!Range)
+ return Cor_RtlImageRvaToSection(NtHeaders, Rva, FileLength);
+
+ NtSection = PTR_IMAGE_FIRST_SECTION( NtHeaders );
+ for (i=0; i<VAL16(NtHeaders->FileHeader.NumberOfSections); i++) {
+ if (FileLength &&
+ ((VAL32(NtSection->PointerToRawData) > FileLength) ||
+ (VAL32(NtSection->SizeOfRawData) > FileLength - VAL32(NtSection->PointerToRawData))))
+ return NULL;
+ if (Rva >= VAL32(NtSection->VirtualAddress) &&
+ Rva + Range <= VAL32(NtSection->VirtualAddress) + VAL32(NtSection->SizeOfRawData))
+ return NtSection;
+
+ ++NtSection;
+ }
+
+ return NULL;
+}
+
+EXTERN_C DWORD Cor_RtlImageRvaToOffset(PTR_IMAGE_NT_HEADERS NtHeaders,
+ ULONG Rva,
+ ULONG FileLength)
+{
+ LIMITED_METHOD_CONTRACT;
+ PIMAGE_SECTION_HEADER NtSection =
+ Cor_RtlImageRvaToSection(NtHeaders,
+ Rva,
+ FileLength);
+
+ if (NtSection)
+ return ((Rva - VAL32(NtSection->VirtualAddress)) +
+ VAL32(NtSection->PointerToRawData));
+ else
+ return NULL;
+}
diff --git a/src/coreclr/utilcode/cycletimer.cpp b/src/coreclr/utilcode/cycletimer.cpp
new file mode 100644
index 00000000000..642ce22e630
--- /dev/null
+++ b/src/coreclr/utilcode/cycletimer.cpp
@@ -0,0 +1,84 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+#include "cycletimer.h"
+#include "winbase.h"
+#include "winwrap.h"
+#include "assert.h"
+#include "utilcode.h"
+
+bool CycleTimer::GetThreadCyclesS(unsigned __int64* cycles)
+{
+ BOOL res = FALSE;
+ res = QueryThreadCycleTime(GetCurrentThread(), cycles);
+ return res != FALSE;
+}
+
+static const int SampleLoopSize = 1000000;
+
+// static
+double CycleTimer::CyclesPerSecond()
+{
+ // Windows does not provide a way of converting cycles to time -- reasonably enough,
+ // since the frequency of a machine may vary, due, e.g., to power management.
+ // Windows *does* allow you to translate QueryPerformanceCounter counts into time,
+ // however. So we'll assume that the clock speed stayed constant, and measure both the
+ // QPC counts and cycles of a short loop, to get a conversion factor.
+ LARGE_INTEGER lpFrequency;
+ if (!QueryPerformanceFrequency(&lpFrequency)) return 0.0;
+ // Otherwise...
+ LARGE_INTEGER qpcStart;
+ unsigned __int64 cycleStart;
+ if (!QueryPerformanceCounter(&qpcStart)) return 0.0;
+ if (!GetThreadCyclesS(&cycleStart)) return 0.0;
+ volatile int sum = 0;
+ for (int k = 0; k < SampleLoopSize; k++)
+ {
+ sum += k;
+ }
+ LARGE_INTEGER qpcEnd;
+ if (!QueryPerformanceCounter(&qpcEnd)) return 0.0;
+ unsigned __int64 cycleEnd;
+ if (!GetThreadCyclesS(&cycleEnd)) return 0.0;
+
+ double qpcTicks = ((double)qpcEnd.QuadPart) - ((double)qpcStart.QuadPart);
+ double secs = (qpcTicks / ((double)lpFrequency.QuadPart));
+ double cycles = ((double)cycleEnd) - ((double)cycleStart);
+ return cycles / secs;
+}
+
+// static
+unsigned __int64 CycleTimer::QueryOverhead()
+{
+ unsigned __int64 tot = 0;
+ unsigned __int64 startCycles;
+ unsigned __int64 endCycles;
+ const int N = 1000;
+ bool b = GetThreadCyclesS(&startCycles); assert(b);
+ for (int i = 0; i < N; i++)
+ {
+ b = GetThreadCyclesS(&endCycles); assert(b);
+ tot += (endCycles-startCycles);
+ startCycles = endCycles;
+ }
+ return tot/N;
+}
+
+// static
+void CycleTimer::InterlockedAddU64(unsigned __int64* loc, unsigned __int64 amount)
+{
+ volatile __int64* vloc = (volatile __int64*)loc;
+ unsigned __int64 prev = *vloc;
+ for (;;)
+ {
+ unsigned __int64 next = prev + amount;
+ __int64 snext = (__int64)next;
+ __int64 sprev = (__int64)prev;
+ __int64 res = InterlockedCompareExchange64(vloc, snext, sprev);
+ if (res == sprev) return;
+ else prev = (unsigned __int64)res;
+ }
+}
+
diff --git a/src/coreclr/utilcode/dacutil.cpp b/src/coreclr/utilcode/dacutil.cpp
new file mode 100644
index 00000000000..914007e6781
--- /dev/null
+++ b/src/coreclr/utilcode/dacutil.cpp
@@ -0,0 +1,246 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+//
+// Internal data access functionality.
+//
+
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include <winwrap.h>
+#include <utilcode.h>
+#include <dacprivate.h>
+
+//----------------------------------------------------------------------------
+//
+// LiveProcDataTarget.
+//
+//----------------------------------------------------------------------------
+
+LiveProcDataTarget::LiveProcDataTarget(HANDLE process,
+ DWORD processId,
+ CLRDATA_ADDRESS baseAddressOfEngine)
+{
+ m_process = process;
+ m_processId = processId;
+ m_baseAddressOfEngine = baseAddressOfEngine;
+}
+
+STDMETHODIMP
+LiveProcDataTarget::QueryInterface(
+ THIS_
+ IN REFIID InterfaceId,
+ OUT PVOID* Interface
+ )
+{
+ if (InterfaceId == IID_IUnknown ||
+ InterfaceId == IID_ICLRDataTarget)
+ {
+ *Interface = (ICLRDataTarget*)this;
+ // No need to refcount as this class is contained.
+ return S_OK;
+ }
+ else
+ {
+ *Interface = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+STDMETHODIMP_(ULONG)
+LiveProcDataTarget::AddRef(
+ THIS
+ )
+{
+ // No need to refcount as this class is contained.
+ return 1;
+}
+
+STDMETHODIMP_(ULONG)
+LiveProcDataTarget::Release(
+ THIS
+ )
+{
+ SUPPORTS_DAC_HOST_ONLY;
+ // No need to refcount as this class is contained.
+ return 0;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetMachineType(
+ /* [out] */ ULONG32 *machine)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if defined(TARGET_X86)
+ *machine = IMAGE_FILE_MACHINE_I386;
+#elif defined(TARGET_AMD64)
+ *machine = IMAGE_FILE_MACHINE_AMD64;
+#elif defined(TARGET_ARM)
+ *machine = IMAGE_FILE_MACHINE_ARMNT;
+#else
+ PORTABILITY_ASSERT("Unknown Processor");
+#endif
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetPointerSize(
+ /* [out] */ ULONG32 *size)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ *size = sizeof(void*);
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetImageBase(
+ /* [string][in] */ LPCWSTR name,
+ /* [out] */ CLRDATA_ADDRESS *base)
+{
+ //
+ // The only image base that the access code cares
+ // about right now is the base of mscorwks.
+ //
+
+ if (wcscmp(name, MAIN_CLR_DLL_NAME_W))
+ {
+ return E_NOINTERFACE;
+ }
+
+ //
+ // If a base address was specified, use that
+ //
+ if (NULL != m_baseAddressOfEngine)
+ {
+ *base = m_baseAddressOfEngine;
+ return S_OK;
+ }
+
+ //
+ // Our creator must have told us WHICH clr to work with.
+ //
+ return E_FAIL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::ReadVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [length_is][size_is][out] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ // ReadProcessMemory will fail if any part of the
+ // region to read does not have read access. This
+ // routine attempts to read the largest valid prefix
+ // so it has to break up reads on page boundaries.
+
+ HRESULT status = S_OK;
+ ULONG32 totalDone = 0;
+ SIZE_T read;
+ ULONG32 readSize;
+
+ while (request > 0)
+ {
+ // Calculate bytes to read and don't let read cross
+ // a page boundary.
+ readSize = GetOsPageSize() - (ULONG32)(address & (GetOsPageSize() - 1));
+ readSize = min(request, readSize);
+
+ if (!ReadProcessMemory(m_process, (PVOID)(ULONG_PTR)address,
+ buffer, readSize, &read))
+ {
+ if (totalDone == 0)
+ {
+ // If we haven't read anything indicate failure.
+ status = E_FAIL;
+ }
+ break;
+ }
+
+ totalDone += (ULONG32)read;
+ address += read;
+ buffer += read;
+ request -= (ULONG32)read;
+ }
+
+ *done = totalDone;
+ return status;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::WriteVirtual(
+ /* [in] */ CLRDATA_ADDRESS address,
+ /* [size_is][in] */ PBYTE buffer,
+ /* [in] */ ULONG32 request,
+ /* [optional][out] */ ULONG32 *done)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [out] */ CLRDATA_ADDRESS* value)
+{
+ SUPPORTS_DAC;
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::SetTLSValue(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 index,
+ /* [in] */ CLRDATA_ADDRESS value)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetCurrentThreadID(
+ /* [out] */ ULONG32* threadID)
+{
+ SUPPORTS_DAC;
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::GetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextFlags,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::SetThreadContext(
+ /* [in] */ ULONG32 threadID,
+ /* [in] */ ULONG32 contextSize,
+ /* [out, size_is(contextSize)] */ PBYTE context)
+{
+ // Not necessary yet.
+ return E_NOTIMPL;
+}
+
+HRESULT STDMETHODCALLTYPE
+LiveProcDataTarget::Request(
+ /* [in] */ ULONG32 reqCode,
+ /* [in] */ ULONG32 inBufferSize,
+ /* [size_is][in] */ BYTE *inBuffer,
+ /* [in] */ ULONG32 outBufferSize,
+ /* [size_is][out] */ BYTE *outBuffer)
+{
+ // None supported.
+ return E_INVALIDARG;
+}
diff --git a/src/coreclr/utilcode/debug.cpp b/src/coreclr/utilcode/debug.cpp
new file mode 100644
index 00000000000..b4f44dc989d
--- /dev/null
+++ b/src/coreclr/utilcode/debug.cpp
@@ -0,0 +1,841 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// Debug.cpp
+//
+// Helper code for debugging.
+//*****************************************************************************
+//
+
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+#include "corexcep.h"
+
+#ifdef _DEBUG
+#define LOGGING
+#endif
+
+
+#include "log.h"
+
+extern "C" _CRTIMP int __cdecl _flushall(void);
+
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled(bool stackoverflow = false);
+#endif
+
+// Global state counter to implement SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE.
+Volatile<LONG> g_DbgSuppressAllocationAsserts = 0;
+
+
+#ifdef _DEBUG
+
+int LowResourceMessageBoxHelperAnsi(
+ LPCSTR szText, // Text message
+ LPCSTR szTitle, // Title
+ UINT uType); // Style of MessageBox
+
+//*****************************************************************************
+// This struct tracks the asserts we want to ignore in the rest of this
+// run of the application.
+//*****************************************************************************
+struct _DBGIGNOREDATA
+{
+ char rcFile[_MAX_PATH];
+ int iLine;
+ bool bIgnore;
+};
+
+typedef CDynArray<_DBGIGNOREDATA> DBGIGNORE;
+static BYTE grIgnoreMemory[sizeof(DBGIGNORE)];
+inline DBGIGNORE* GetDBGIGNORE()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ static bool fInit; // = false;
+ if (!fInit)
+ {
+ SCAN_IGNORE_THROW; // Doesn't really throw here.
+ new (grIgnoreMemory) CDynArray<_DBGIGNOREDATA>();
+ fInit = true;
+ }
+
+ return (DBGIGNORE*)grIgnoreMemory;
+}
+
+// Continue the app on an assert. Still output the assert, but
+// Don't throw up a GUI. This is useful for testing fatal error
+// paths (like FEEE) where the runtime asserts.
+BOOL ContinueOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ConfigDWORD fNoGui;
+ return fNoGui.val(CLRConfig::INTERNAL_ContinueOnAssert);
+}
+
+void DoRaiseExceptionOnAssert(DWORD chance)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if !defined(DACCESS_COMPILE)
+ if (chance)
+ {
+#ifndef TARGET_UNIX
+ PAL_TRY_NAKED
+ {
+ RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
+ }
+ PAL_EXCEPT_NAKED((chance == 1) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ }
+ PAL_ENDTRY_NAKED
+#else // TARGET_UNIX
+ // For PAL always raise the exception.
+ RaiseException(EXCEPTION_INTERNAL_ASSERT, 0, 0, NULL);
+#endif // TARGET_UNIX
+ }
+#endif // !DACCESS_COMPILE
+}
+
+enum RaiseOnAssertOptions { rTestAndRaise, rTestOnly };
+
+BOOL RaiseExceptionOnAssert(RaiseOnAssertOptions option = rTestAndRaise)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // ok for debug-only code to take locks
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ DWORD fRet = 0;
+
+#if !defined(DACCESS_COMPILE)
+ static ConfigDWORD fRaiseExceptionOnAssert;
+ //
+ // we don't want this config key to affect mscordacwks as well!
+ //
+ EX_TRY
+ {
+ fRet = fRaiseExceptionOnAssert.val(CLRConfig::INTERNAL_RaiseExceptionOnAssert);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (option == rTestAndRaise && fRet != 0)
+ {
+ DoRaiseExceptionOnAssert(fRet);
+ }
+#endif // !DACCESS_COMPILE
+
+ return fRet != 0;
+}
+
+BOOL DebugBreakOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // ok for debug-only code to take locks
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ BOOL fRet = FALSE;
+
+#ifndef DACCESS_COMPILE
+ static ConfigDWORD fDebugBreak;
+ //
+ // we don't want this config key to affect mscordacwks as well!
+ //
+ EX_TRY
+ {
+ fRet = fDebugBreak.val(CLRConfig::INTERNAL_DebugBreakOnAssert);
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // DACCESS_COMPILE
+
+ return fRet;
+}
+
+VOID TerminateOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ ShutdownLogging();
+#ifdef HOST_WINDOWS
+ CreateCrashDumpIfEnabled();
+#endif
+ RaiseFailFastException(NULL, NULL, 0);
+}
+
+thread_local bool f_bDisplayingAssertDlg;
+
+VOID LogAssert(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // Log asserts to the stress log. Note that we can't include the szExpr b/c that
+ // may not be a string literal (particularly for formatt-able asserts).
+ STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
+
+ SYSTEMTIME st;
+#ifndef TARGET_UNIX
+ GetLocalTime(&st);
+#else
+ GetSystemTime(&st);
+#endif
+
+ PathString exename;
+ WszGetModuleFileName(NULL, exename);
+
+ LOG((LF_ASSERT,
+ LL_FATALERROR,
+ "FAILED ASSERT(PID %d [0x%08x], Thread: %d [0x%x]) (%lu/%lu/%lu: %02lu:%02lu:%02lu %s): File: %s, Line %d : %s\n",
+ GetCurrentProcessId(),
+ GetCurrentProcessId(),
+ GetCurrentThreadId(),
+ GetCurrentThreadId(),
+ (ULONG)st.wMonth,
+ (ULONG)st.wDay,
+ (ULONG)st.wYear,
+ 1 + (( (ULONG)st.wHour + 11 ) % 12),
+ (ULONG)st.wMinute,
+ (ULONG)st.wSecond,
+ (st.wHour < 12) ? "am" : "pm",
+ szFile,
+ iLine,
+ szExpr));
+ LOG((LF_ASSERT, LL_FATALERROR, "RUNNING EXE: %ws\n", exename.GetUnicode()));
+}
+
+//*****************************************************************************
+
+BOOL LaunchJITDebugger()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ BOOL fSuccess = FALSE;
+#ifndef TARGET_UNIX
+ EX_TRY
+ {
+ SString debugger;
+ GetDebuggerSettingInfo(debugger, NULL);
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ // We can leave this event as it is since it is inherited by a child process.
+ // We will block one scheduler, but the process is asking a user if they want to attach debugger.
+ HandleHolder eventHandle = WszCreateEvent(&sa, TRUE, FALSE, NULL);
+ if (eventHandle == NULL)
+ ThrowOutOfMemory();
+
+ SString cmdLine;
+ cmdLine.Printf(debugger, GetCurrentProcessId(), eventHandle.GetValue());
+
+ STARTUPINFO StartupInfo;
+ memset(&StartupInfo, 0, sizeof(StartupInfo));
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.lpDesktop = const_cast<LPWSTR>(W("Winsta0\\Default"));
+
+ PROCESS_INFORMATION ProcessInformation;
+ if (WszCreateProcess(NULL, cmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartupInfo, &ProcessInformation))
+ {
+ WaitForSingleObject(eventHandle.GetValue(), INFINITE);
+ }
+
+ fSuccess = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // !TARGET_UNIX
+ return fSuccess;
+}
+
+
+//*****************************************************************************
+// This function is called in order to ultimately return an out of memory
+// failed hresult. But this code will check what environment you are running
+// in and give an assert for running in a debug build environment. Usually
+// out of memory on a dev machine is a bogus allocation, and this allows you
+// to catch such errors. But when run in a stress envrionment where you are
+// trying to get out of memory, assert behavior stops the tests.
+//*****************************************************************************
+HRESULT _OutOfMemory(LPCSTR szFile, int iLine)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ DbgWriteEx(W("WARNING: Out of memory condition being issued from: %hs, line %d\n"),
+ szFile, iLine);
+ return (E_OUTOFMEMORY);
+}
+
+int _DbgBreakCount = 0;
+static const char * szLowMemoryAssertMessage = "Assert failure (unable to format)";
+
+//*****************************************************************************
+// This function will handle ignore codes and tell the user what is happening.
+//*****************************************************************************
+bool _DbgBreakCheck(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ DBGIGNORE* pDBGIFNORE = GetDBGIGNORE();
+ _DBGIGNOREDATA *psData;
+ int i;
+
+ // Check for ignore all.
+ for (i = 0, psData = pDBGIFNORE->Ptr(); i < pDBGIFNORE->Count(); i++, psData++)
+ {
+ if (psData->iLine == iLine && SString::_stricmp(psData->rcFile, szFile) == 0 && psData->bIgnore == true)
+ {
+ return false;
+ }
+ }
+
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | TakesLockViolation);
+
+ SString debugOutput;
+ SString dialogOutput;
+ SString modulePath;
+ SString dialogTitle;
+ SString dialogIgnoreMessage;
+ BOOL formattedMessages = FALSE;
+
+ // If we are low on memory we cannot even format a message. If this happens we want to
+ // contain the exception here but display as much information as we can about the exception.
+ if (!fConstrained)
+ {
+ EX_TRY
+ {
+ ClrGetModuleFileName(0, modulePath);
+ debugOutput.Printf(
+ W("\nAssert failure(PID %d [0x%08x], Thread: %d [0x%04x]): %hs\n")
+ W(" File: %hs Line: %d\n")
+ W(" Image: "),
+ GetCurrentProcessId(), GetCurrentProcessId(),
+ GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+ debugOutput.Append(modulePath);
+ debugOutput.Append(W("\n\n"));
+
+ // Change format for message box. The extra spaces in the title
+ // are there to get around format truncation.
+ dialogOutput.Printf(
+ W("%hs\n\n%hs, Line: %d\n\nAbort - Kill program\nRetry - Debug\nIgnore - Keep running\n")
+ W("\n\nImage:\n"), szExpr, szFile, iLine);
+ dialogOutput.Append(modulePath);
+ dialogOutput.Append(W("\n"));
+ dialogTitle.Printf(W("Assert Failure (PID %d, Thread %d/0x%04x)"),
+ GetCurrentProcessId(), GetCurrentThreadId(), GetCurrentThreadId());
+
+ dialogIgnoreMessage.Printf(W("Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n\n%hs\nLine: %d\n"),
+ szFile, iLine);
+
+ formattedMessages = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+
+ // Emit assert in debug output and console for easy access.
+ if (formattedMessages)
+ {
+ WszOutputDebugString(debugOutput);
+ fwprintf(stderr, W("%s"), (const WCHAR*)debugOutput);
+ }
+ else
+ {
+ // Note: we cannot convert to unicode or concatenate in this situation.
+ OutputDebugStringA(szLowMemoryAssertMessage);
+ OutputDebugStringA("\n");
+ OutputDebugStringA(szFile);
+ OutputDebugStringA("\n");
+ OutputDebugStringA(szExpr);
+ OutputDebugStringA("\n");
+ printf(szLowMemoryAssertMessage);
+ printf("\n");
+ printf(szFile);
+ printf("\n");
+ printf("%s", szExpr);
+ printf("\n");
+ }
+
+ LogAssert(szFile, iLine, szExpr);
+ FlushLogging(); // make certain we get the last part of the log
+ _flushall();
+
+ if (ContinueOnAssert())
+ {
+ return false; // don't stop debugger. No gui.
+ }
+
+ if (IsDebuggerPresent() || DebugBreakOnAssert())
+ {
+ return true; // like a retry
+ }
+
+ if (NoGuiOnAssert())
+ {
+ TerminateOnAssert();
+ }
+
+ if (f_bDisplayingAssertDlg)
+ {
+ // We are already displaying an assert dialog box on this thread. The reason why we came here is
+ // the message loop run by the API we call to display the UI. A message was dispatched and execution
+ // ended up in the runtime where it fired another assertion. If this happens before the dialog had
+ // a chance to fully initialize the original assert may not be visible which is misleading for the
+ // user. So we just continue.
+ return false;
+ }
+
+ f_bDisplayingAssertDlg = true;
+
+ // Tell user there was an error.
+ _DbgBreakCount++;
+ int ret;
+ if (formattedMessages)
+ {
+ ret = UtilMessageBoxCatastrophicNonLocalized(
+ W("%s"), dialogTitle, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION, TRUE, (const WCHAR*)dialogOutput);
+ }
+ else
+ {
+ ret = LowResourceMessageBoxHelperAnsi(
+ szExpr, szLowMemoryAssertMessage, MB_ABORTRETRYIGNORE | MB_ICONEXCLAMATION);
+ }
+ --_DbgBreakCount;
+
+ f_bDisplayingAssertDlg = false;
+
+ switch(ret)
+ {
+ case 0:
+#if 0
+ // The message box was not displayed. Tell caller to break.
+ return true;
+#endif
+ // For abort, just quit the app.
+ case IDABORT:
+#ifdef HOST_WINDOWS
+ CreateCrashDumpIfEnabled();
+#endif
+ TerminateProcess(GetCurrentProcess(), 1);
+ break;
+
+ // Tell caller to break at the correct location.
+ case IDRETRY:
+ if (IsDebuggerPresent())
+ {
+ SetErrorMode(0);
+ }
+ else
+ {
+ LaunchJITDebugger();
+ }
+ return true;
+
+ // If we want to ignore the assert, find out if this is forever.
+ case IDIGNORE:
+ if (formattedMessages)
+ {
+ if (UtilMessageBoxCatastrophicNonLocalized(
+ dialogIgnoreMessage,
+ W("Ignore Assert Forever?"),
+ MB_ICONQUESTION | MB_YESNO,
+ TRUE) != IDYES)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (LowResourceMessageBoxHelperAnsi(
+ "Ignore the assert for the rest of this run?\nYes - Assert will never fire again.\nNo - Assert will continue to fire.\n",
+ "Ignore Assert Forever?",
+ MB_ICONQUESTION | MB_YESNO) != IDYES)
+ {
+ break;
+ }
+ }
+ if ((psData = pDBGIFNORE->Append()) == 0)
+ {
+ return false;
+ }
+ psData->bIgnore = true;
+ psData->iLine = iLine;
+ strcpy(psData->rcFile, szFile);
+ break;
+ }
+
+ return false;
+}
+
+bool _DbgBreakCheckNoThrow(
+ LPCSTR szFile,
+ int iLine,
+ LPCSTR szExpr,
+ BOOL fConstrained)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ bool failed = false;
+ bool result = false;
+ EX_TRY
+ {
+ result = _DbgBreakCheck(szFile, iLine, szExpr, fConstrained);
+ }
+ EX_CATCH
+ {
+ failed = true;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (failed)
+ {
+ return true;
+ }
+ return result;
+}
+
+#ifndef TARGET_UNIX
+// Get the timestamp from the PE file header. This is useful
+unsigned DbgGetEXETimeStamp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ULONG cache = 0;
+ if (cache == 0) {
+ // Use GetModuleHandleA to avoid contracts - this results in a recursive loop initializing the
+ // debug allocator.
+ BYTE* imageBase = (BYTE*) GetModuleHandleA(NULL);
+ if (imageBase == 0)
+ return(0);
+ IMAGE_DOS_HEADER *pDOS = (IMAGE_DOS_HEADER*) imageBase;
+ if ((pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)) || (pDOS->e_lfanew == 0))
+ return(0);
+
+ IMAGE_NT_HEADERS *pNT = (IMAGE_NT_HEADERS*) (VAL32(pDOS->e_lfanew) + imageBase);
+ cache = VAL32(pNT->FileHeader.TimeDateStamp);
+ }
+
+ return cache;
+}
+#endif // TARGET_UNIX
+
+// Called from within the IfFail...() macros. Set a breakpoint here to break on
+// errors.
+VOID DebBreak()
+{
+ STATIC_CONTRACT_LEAF;
+ static int i = 0; // add some code here so that we'll be able to set a BP
+ i++;
+}
+
+VOID DebBreakHr(HRESULT hr)
+{
+ STATIC_CONTRACT_LEAF;
+ static int i = 0; // add some code here so that we'll be able to set a BP
+ _ASSERTE(hr != (HRESULT) 0xcccccccc);
+ i++;
+
+ // @CONSIDER: We maybe want this on retail builds.
+ #ifdef _DEBUG
+ static DWORD dwBreakHR = 99;
+
+ if (dwBreakHR == 99)
+ dwBreakHR = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnHR);
+ if (dwBreakHR == (DWORD)hr)
+ {
+ _DbgBreak();
+ }
+ #endif
+}
+
+#ifndef TARGET_UNIX
+CHAR g_szExprWithStack2[10480];
+#endif
+void *dbgForceToMemory; // dummy pointer that pessimises enregistration
+
+int g_BufferLock = -1;
+
+VOID DbgAssertDialog(const char *szFile, int iLine, const char *szExpr)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC_HOST_ONLY;
+
+ DEBUG_ONLY_FUNCTION;
+
+#ifdef DACCESS_COMPILE
+ // In the DAC case, asserts can mean one of two things.
+ // Either there is a bug in the DAC infrastructure itself (a real assert), or just
+ // that the target is corrupt or being accessed at an inconsistent state (a "target
+ // consistency failure"). For target consistency failures, we need a mechanism to disable them
+ // (without affecting other asserts) so that we can test corrupt / inconsistent targets.
+
+ // @dbgtodo DAC: For now we're treating all asserts as if they are target consistency checks.
+ // In the future we should differentiate the two so that real asserts continue to fire, even when
+ // we expect the target to be inconsistent. See DevDiv Bugs 31674.
+ if( !DacTargetConsistencyAssertsEnabled() )
+ {
+ return;
+ }
+#endif // #ifndef DACCESS_COMPILE
+
+ // We increment this every time we use the SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE
+ // macro below. If it is a big number it means either a lot of threads are asserting
+ // or we have a recursion in the Assert logic (usually the latter). At least with this
+ // code in place, we don't get stack overflow (and the process torn down).
+ // the correct fix is to avoid calling asserting when allocating memory with an assert.
+ if (g_DbgSuppressAllocationAsserts > 16)
+ DebugBreak();
+
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+
+ // Raising the assert dialog can cause us to re-enter the host when allocating
+ // memory for the string. Since this is debug-only code, we can safely skip
+ // violation asserts here, particularly since they can also cause infinite
+ // recursion.
+ PERMANENT_CONTRACT_VIOLATION(HostViolation, ReasonDebugOnly);
+
+ dbgForceToMemory = &szFile; //make certain these args are available in the debugger
+ dbgForceToMemory = &iLine;
+ dbgForceToMemory = &szExpr;
+
+ RaiseExceptionOnAssert(rTestAndRaise);
+
+ BOOL fConstrained = FALSE;
+
+ DWORD dwAssertStacktrace = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_AssertStacktrace);
+
+ LONG lAlreadyOwned = InterlockedExchange((LPLONG)&g_BufferLock, 1);
+ if (fConstrained || dwAssertStacktrace == 0 || lAlreadyOwned == 1)
+ {
+ if (_DbgBreakCheckNoThrow(szFile, iLine, szExpr, fConstrained))
+ {
+ _DbgBreak();
+ }
+ }
+ else
+ {
+ char *szExprToDisplay = (char*)szExpr;
+#ifdef TARGET_UNIX
+ BOOL fGotStackTrace = TRUE;
+#else
+ BOOL fGotStackTrace = FALSE;
+#ifndef DACCESS_COMPILE
+ EX_TRY
+ {
+ FAULT_NOT_FATAL();
+ szExprToDisplay = &g_szExprWithStack2[0];
+ strcpy(szExprToDisplay, szExpr);
+ strcat_s(szExprToDisplay, _countof(g_szExprWithStack2), "\n\n");
+ GetStringFromStackLevels(1, 10, szExprToDisplay + strlen(szExprToDisplay));
+ fGotStackTrace = TRUE;
+ }
+ EX_CATCH
+ {
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // DACCESS_COMPILE
+#endif // TARGET_UNIX
+
+ if (_DbgBreakCheckNoThrow(szFile, iLine, szExprToDisplay, !fGotStackTrace))
+ {
+ _DbgBreak();
+ }
+
+ g_BufferLock = 0;
+ }
+} // DbgAssertDialog
+
+#if !defined(DACCESS_COMPILE)
+//-----------------------------------------------------------------------------
+// Returns an a stacktrace for a given context.
+// Very useful inside exception filters.
+// Returns true if successful, false on failure (such as OOM).
+// This never throws.
+//-----------------------------------------------------------------------------
+bool GetStackTraceAtContext(SString & s, CONTEXT * pContext)
+{
+ SUPPRESS_ALLOCATION_ASSERTS_IN_THIS_SCOPE;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ // NULL means use the current context.
+ bool fSuccess = false;
+
+ FAULT_NOT_FATAL();
+
+#ifndef TARGET_UNIX
+ EX_TRY
+ {
+ const int cTotal = cfrMaxAssertStackLevels - 1;
+ // If we have a supplied context, then don't skip any frames. Else we'll
+ // be using the current context, so skip this frame.
+ const int cSkip = (pContext == NULL) ? 1 : 0;
+ char * szString = s.OpenANSIBuffer(cchMaxAssertStackLevelStringLen * cTotal);
+ GetStringFromStackLevels(cSkip, cTotal, szString, pContext);
+ s.CloseBuffer((COUNT_T) strlen(szString));
+
+ // If we made it this far w/o throwing, we succeeded.
+ fSuccess = true;
+ }
+ EX_CATCH
+ {
+ // Nothing to do here.
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+#endif // TARGET_UNIX
+
+ return fSuccess;
+} // GetStackTraceAtContext
+#endif // !defined(DACCESS_COMPILE)
+#endif // _DEBUG
+
+BOOL NoGuiOnAssert()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_DEBUG_ONLY;
+
+ static ConfigDWORD fNoGui;
+ return fNoGui.val(CLRConfig::INTERNAL_NoGuiOnAssert);
+}
+
+// This helper will throw up a message box without allocating or using stack if possible, and is
+// appropriate for either low memory or low stack situations.
+int LowResourceMessageBoxHelperAnsi(
+ LPCSTR szText, // Text message
+ LPCSTR szTitle, // Title
+ UINT uType) // Style of MessageBox
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+ }
+ CONTRACTL_END;
+
+ // In low memory or stack constrained code we cannot format or convert strings, so use the
+ // ANSI version.
+ int result = MessageBoxA(NULL, szText, szTitle, uType);
+ return result;
+}
+
+
+/****************************************************************************
+ The following two functions are defined to allow Free builds to call
+ DebugBreak or to Assert with a stack trace for unexpected fatal errors.
+ Typically these paths are enabled via a registry key in a Free Build
+*****************************************************************************/
+
+VOID __FreeBuildDebugBreak()
+{
+ WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
+
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_BreakOnRetailAssert))
+ {
+ DebugBreak();
+ }
+}
+
+void *freForceToMemory; // dummy pointer that pessimises enregistration
+
+void DECLSPEC_NORETURN __FreeBuildAssertFail(const char *szFile, int iLine, const char *szExpr)
+{
+ WRAPPER_NO_CONTRACT; // If we're calling this, we're well past caring about contract consistency!
+
+ freForceToMemory = &szFile; //make certain these args are available in the debugger
+ freForceToMemory = &iLine;
+ freForceToMemory = &szExpr;
+
+ __FreeBuildDebugBreak();
+
+ SString buffer;
+ SString modulePath;
+
+ // Give assert in output for easy access.
+ ClrGetModuleFileName(0, modulePath);
+#ifndef TARGET_UNIX
+ buffer.Printf(W("CLR: Assert failure(PID %d [0x%08x], Thread: %d [0x%x]): %hs\n")
+ W(" File: %hs, Line: %d Image:\n"),
+ GetCurrentProcessId(), GetCurrentProcessId(),
+ GetCurrentThreadId(), GetCurrentThreadId(),
+ szExpr, szFile, iLine);
+ buffer.Append(modulePath);
+ buffer.Append(W("\n"));
+ WszOutputDebugString(buffer);
+ // Write out the error to the console
+ _putws(buffer);
+#else // TARGET_UNIX
+ // UNIXTODO: Do this for Unix.
+#endif // TARGET_UNIX
+ // Log to the stress log. Note that we can't include the szExpr b/c that
+ // may not be a string literal (particularly for formatt-able asserts).
+ STRESS_LOG2(LF_ASSERT, LL_ALWAYS, "ASSERT:%s, line:%d\n", szFile, iLine);
+
+ FlushLogging(); // make certain we get the last part of the log
+
+ _flushall();
+
+ ShutdownLogging();
+
+#ifdef HOST_WINDOWS
+ CreateCrashDumpIfEnabled();
+#endif
+ RaiseFailFastException(NULL, NULL, 0);
+
+ UNREACHABLE();
+}
diff --git a/src/coreclr/utilcode/dlwrap.cpp b/src/coreclr/utilcode/dlwrap.cpp
new file mode 100644
index 00000000000..8cb312f8f81
--- /dev/null
+++ b/src/coreclr/utilcode/dlwrap.cpp
@@ -0,0 +1,77 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "metadata.h"
+#include "ex.h"
+#include "pedecoder.h"
+
+#include <wininet.h>
+#include <urlmon.h>
+
+DWORD
+GetFileVersionInfoSizeW_NoThrow(
+ LPCWSTR lptstrFilename, /* Filename of version stamped file */
+ LPDWORD lpdwHandle
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ DWORD dwRet=0;
+ EX_TRY
+ {
+ dwRet=GetFileVersionInfoSize( (LPWSTR)lptstrFilename, lpdwHandle );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return dwRet;
+
+}
+
+BOOL
+GetFileVersionInfoW_NoThrow(
+ LPCWSTR lptstrFilename, /* Filename of version stamped file */
+ DWORD dwHandle, /* Information from GetFileVersionSize */
+ DWORD dwLen, /* Length of buffer for info */
+ LPVOID lpData
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=GetFileVersionInfo( (LPWSTR)lptstrFilename, dwHandle,dwLen,lpData );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
+BOOL
+VerQueryValueW_NoThrow(
+ const LPVOID pBlock,
+ LPCWSTR lpSubBlock,
+ LPVOID * lplpBuffer,
+ PUINT puLen
+ )
+{
+ WRAPPER_NO_CONTRACT;
+ HRESULT hr=S_OK;
+ BOOL bRet=FALSE;
+ EX_TRY
+ {
+ bRet=VerQueryValueW( pBlock, (LPWSTR)lpSubBlock,lplpBuffer,puLen );
+ }
+ EX_CATCH_HRESULT(hr);
+ if (hr!=S_OK)
+ SetLastError(hr);
+ return bRet;
+
+}
+
diff --git a/src/coreclr/utilcode/ex.cpp b/src/coreclr/utilcode/ex.cpp
new file mode 100644
index 00000000000..7b15e4a98a2
--- /dev/null
+++ b/src/coreclr/utilcode/ex.cpp
@@ -0,0 +1,1358 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+
+//
+// ---------------------------------------------------------------------------
+// Ex.cpp
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "string.h"
+#include "ex.h"
+#include "holder.h"
+
+// error codes
+#include "corerror.h"
+
+#include "../dlls/mscorrc/resource.h"
+
+#include "olectl.h"
+
+#include "corexcep.h"
+
+#define MAX_EXCEPTION_MSG 200
+
+// Set if fatal error (like stack overflow or out of memory) occurred in this process.
+GVAL_IMPL_INIT(HRESULT, g_hrFatalError, S_OK);
+
+// Helper function to get an exception object from outside the exception. In
+// the CLR, it may be from the Thread object. Non-CLR users have no thread object,
+// and it will do nothing.
+void GetLastThrownObjectExceptionFromThread(Exception **ppException);
+
+// Helper function to get pointer to clr module base
+void* GetClrModuleBase();
+
+Exception *Exception::g_OOMException = NULL;
+
+// avoid global constructors
+static BYTE g_OOMExceptionInstance[sizeof(OutOfMemoryException)];
+
+Exception * Exception::GetOOMException()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (!g_OOMException) {
+ // Create a local copy on the stack and then copy it over to the static instance.
+ // This avoids race conditions caused by multiple initializations of vtable in the constructor
+
+ OutOfMemoryException local(TRUE); // Construct a "preallocated" instance.
+ memcpy((void*)&g_OOMExceptionInstance, (void*)&local, sizeof(OutOfMemoryException));
+
+ g_OOMException = (OutOfMemoryException*)&g_OOMExceptionInstance;
+ }
+
+ return g_OOMException;
+}
+
+/*virtual*/ Exception *OutOfMemoryException::Clone()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return GetOOMException();
+}
+
+//------------------------------------------------------------------------------
+void Exception::Delete(Exception* pvMemory)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ SUPPORTS_DAC_HOST_ONLY; // Exceptions aren't currently marshalled by DAC - just used in the host
+ }
+ CONTRACTL_END;
+
+ if ((pvMemory == 0) || pvMemory->IsPreallocatedException())
+ {
+ return;
+ }
+
+ ::delete((Exception *) pvMemory);
+}
+
+void Exception::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return GenerateTopLevelHRExceptionMessage(GetHR(), result);
+}
+
+void HRMsgException::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_msg.IsEmpty())
+ HRException::GetMessage(result);
+ else
+ result = m_msg;
+}
+
+Exception *Exception::Clone()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ NewHolder<Exception> retExcep(CloneHelper());
+ if (m_innerException)
+ {
+ retExcep->m_innerException = m_innerException->Clone();
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+}
+
+Exception *Exception::CloneHelper()
+{
+ StackSString s;
+ GetMessage(s);
+ return new HRMsgException(GetHR(), s);
+}
+
+Exception *Exception::DomainBoundClone()
+{
+ CONTRACTL
+ {
+ // Because we may call DomainBoundCloneHelper() of ObjrefException or CLRLastThrownObjectException
+ // this should be GC_TRIGGERS, but we can not include EE contracts in Utilcode.
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ NewHolder<Exception> retExcep(DomainBoundCloneHelper());
+ if (m_innerException)
+ {
+ retExcep->m_innerException = m_innerException->DomainBoundClone();
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+}
+
+BOOL Exception::IsTerminal()
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+
+ // CLRException::GetHR() can eventually call BaseDomain::CreateHandle(),
+ // which can indirectly cause a lock if we get a miss in the handle table
+ // cache (TableCacheMissOnAlloc). Since CLRException::GetHR() is virtual,
+ // SCAN won't find this for you (though 40 minutes of one of the sql stress
+ // tests will :-))
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = GetHR();
+ return (COR_E_THREADABORTED == hr);
+}
+
+BOOL Exception::IsTransient()
+{
+ WRAPPER_NO_CONTRACT;
+
+ return IsTransient(GetHR());
+}
+
+/* static */
+BOOL Exception::IsTransient(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return (hr == COR_E_THREADABORTED
+ || hr == COR_E_THREADINTERRUPTED
+ || hr == COR_E_THREADSTOP
+ || hr == COR_E_APPDOMAINUNLOADED
+ || hr == E_OUTOFMEMORY
+ || hr == HRESULT_FROM_WIN32(ERROR_COMMITMENT_LIMIT) // ran out of room in pagefile
+ || hr == HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY)
+ || hr == (HRESULT)STATUS_NO_MEMORY
+ || hr == COR_E_STACKOVERFLOW
+ || hr == MSEE_E_ASSEMBLYLOADINPROGRESS);
+}
+
+//------------------------------------------------------------------------------
+// Functions to manage the preallocated exceptions.
+// Virtual
+BOOL Exception::IsPreallocatedException()
+{ // Most exceptions can't be preallocated. If they can be, their class
+ // should provide a virtual override of this function.
+ return FALSE;
+}
+
+BOOL Exception::IsPreallocatedOOMException()
+{ // This is the preallocated OOM if it is preallocated and is OOM.
+ return IsPreallocatedException() && (GetInstanceType() == OutOfMemoryException::GetType());
+}
+
+//------------------------------------------------------------------------------
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+LPCSTR Exception::GetHRSymbolicName(HRESULT hr)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#define CASE_HRESULT(hrname) case hrname: return #hrname;
+
+ switch (hr)
+ {
+ CASE_HRESULT(S_OK)// 0x00000000L
+ CASE_HRESULT(S_FALSE)// 0x00000001L
+
+ CASE_HRESULT(E_UNEXPECTED)// 0x8000FFFFL
+ CASE_HRESULT(E_NOTIMPL)// 0x80004001L
+ CASE_HRESULT(E_OUTOFMEMORY)// 0x8007000EL
+ CASE_HRESULT(E_INVALIDARG)// 0x80070057L
+ CASE_HRESULT(E_NOINTERFACE)// 0x80004002L
+ CASE_HRESULT(E_POINTER)// 0x80004003L
+ CASE_HRESULT(E_HANDLE)// 0x80070006L
+ CASE_HRESULT(E_ABORT)// 0x80004004L
+ CASE_HRESULT(E_FAIL)// 0x80004005L
+ CASE_HRESULT(E_ACCESSDENIED)// 0x80070005L
+
+#ifdef FEATURE_COMINTEROP
+ CASE_HRESULT(CO_E_INIT_TLS)// 0x80004006L
+ CASE_HRESULT(CO_E_INIT_SHARED_ALLOCATOR)// 0x80004007L
+ CASE_HRESULT(CO_E_INIT_MEMORY_ALLOCATOR)// 0x80004008L
+ CASE_HRESULT(CO_E_INIT_CLASS_CACHE)// 0x80004009L
+ CASE_HRESULT(CO_E_INIT_RPC_CHANNEL)// 0x8000400AL
+ CASE_HRESULT(CO_E_INIT_TLS_SET_CHANNEL_CONTROL)// 0x8000400BL
+ CASE_HRESULT(CO_E_INIT_TLS_CHANNEL_CONTROL)// 0x8000400CL
+ CASE_HRESULT(CO_E_INIT_UNACCEPTED_USER_ALLOCATOR)// 0x8000400DL
+ CASE_HRESULT(CO_E_INIT_SCM_MUTEX_EXISTS)// 0x8000400EL
+ CASE_HRESULT(CO_E_INIT_SCM_FILE_MAPPING_EXISTS)// 0x8000400FL
+ CASE_HRESULT(CO_E_INIT_SCM_MAP_VIEW_OF_FILE)// 0x80004010L
+ CASE_HRESULT(CO_E_INIT_SCM_EXEC_FAILURE)// 0x80004011L
+ CASE_HRESULT(CO_E_INIT_ONLY_SINGLE_THREADED)// 0x80004012L
+
+// ******************
+// FACILITY_ITF
+// ******************
+
+ CASE_HRESULT(OLE_E_OLEVERB)// 0x80040000L
+ CASE_HRESULT(OLE_E_ADVF)// 0x80040001L
+ CASE_HRESULT(OLE_E_ENUM_NOMORE)// 0x80040002L
+ CASE_HRESULT(OLE_E_ADVISENOTSUPPORTED)// 0x80040003L
+ CASE_HRESULT(OLE_E_NOCONNECTION)// 0x80040004L
+ CASE_HRESULT(OLE_E_NOTRUNNING)// 0x80040005L
+ CASE_HRESULT(OLE_E_NOCACHE)// 0x80040006L
+ CASE_HRESULT(OLE_E_BLANK)// 0x80040007L
+ CASE_HRESULT(OLE_E_CLASSDIFF)// 0x80040008L
+ CASE_HRESULT(OLE_E_CANT_GETMONIKER)// 0x80040009L
+ CASE_HRESULT(OLE_E_CANT_BINDTOSOURCE)// 0x8004000AL
+ CASE_HRESULT(OLE_E_STATIC)// 0x8004000BL
+ CASE_HRESULT(OLE_E_PROMPTSAVECANCELLED)// 0x8004000CL
+ CASE_HRESULT(OLE_E_INVALIDRECT)// 0x8004000DL
+ CASE_HRESULT(OLE_E_WRONGCOMPOBJ)// 0x8004000EL
+ CASE_HRESULT(OLE_E_INVALIDHWND)// 0x8004000FL
+ CASE_HRESULT(OLE_E_NOT_INPLACEACTIVE)// 0x80040010L
+ CASE_HRESULT(OLE_E_CANTCONVERT)// 0x80040011L
+ CASE_HRESULT(OLE_E_NOSTORAGE)// 0x80040012L
+ CASE_HRESULT(DV_E_FORMATETC)// 0x80040064L
+ CASE_HRESULT(DV_E_DVTARGETDEVICE)// 0x80040065L
+ CASE_HRESULT(DV_E_STGMEDIUM)// 0x80040066L
+ CASE_HRESULT(DV_E_STATDATA)// 0x80040067L
+ CASE_HRESULT(DV_E_LINDEX)// 0x80040068L
+ CASE_HRESULT(DV_E_TYMED)// 0x80040069L
+ CASE_HRESULT(DV_E_CLIPFORMAT)// 0x8004006AL
+ CASE_HRESULT(DV_E_DVASPECT)// 0x8004006BL
+ CASE_HRESULT(DV_E_DVTARGETDEVICE_SIZE)// 0x8004006CL
+ CASE_HRESULT(DV_E_NOIVIEWOBJECT)// 0x8004006DL
+ CASE_HRESULT(DRAGDROP_E_NOTREGISTERED)// 0x80040100L
+ CASE_HRESULT(DRAGDROP_E_ALREADYREGISTERED)// 0x80040101L
+ CASE_HRESULT(DRAGDROP_E_INVALIDHWND)// 0x80040102L
+ CASE_HRESULT(CLASS_E_NOAGGREGATION)// 0x80040110L
+ CASE_HRESULT(CLASS_E_CLASSNOTAVAILABLE)// 0x80040111L
+ CASE_HRESULT(VIEW_E_DRAW)// 0x80040140L
+ CASE_HRESULT(REGDB_E_READREGDB)// 0x80040150L
+ CASE_HRESULT(REGDB_E_WRITEREGDB)// 0x80040151L
+ CASE_HRESULT(REGDB_E_KEYMISSING)// 0x80040152L
+ CASE_HRESULT(REGDB_E_INVALIDVALUE)// 0x80040153L
+ CASE_HRESULT(REGDB_E_CLASSNOTREG)// 0x80040154L
+ CASE_HRESULT(CACHE_E_NOCACHE_UPDATED)// 0x80040170L
+ CASE_HRESULT(OLEOBJ_E_NOVERBS)// 0x80040180L
+ CASE_HRESULT(INPLACE_E_NOTUNDOABLE)// 0x800401A0L
+ CASE_HRESULT(INPLACE_E_NOTOOLSPACE)// 0x800401A1L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_GET)// 0x800401C0L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_PUT)// 0x800401C1L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_FMT)// 0x800401C2L
+ CASE_HRESULT(CONVERT10_E_OLESTREAM_BITMAP_TO_DIB)// 0x800401C3L
+ CASE_HRESULT(CONVERT10_E_STG_FMT)// 0x800401C4L
+ CASE_HRESULT(CONVERT10_E_STG_NO_STD_STREAM)// 0x800401C5L
+ CASE_HRESULT(CONVERT10_E_STG_DIB_TO_BITMAP)// 0x800401C6L
+ CASE_HRESULT(CLIPBRD_E_CANT_OPEN)// 0x800401D0L
+ CASE_HRESULT(CLIPBRD_E_CANT_EMPTY)// 0x800401D1L
+ CASE_HRESULT(CLIPBRD_E_CANT_SET)// 0x800401D2L
+ CASE_HRESULT(CLIPBRD_E_BAD_DATA)// 0x800401D3L
+ CASE_HRESULT(CLIPBRD_E_CANT_CLOSE)// 0x800401D4L
+ CASE_HRESULT(MK_E_CONNECTMANUALLY)// 0x800401E0L
+ CASE_HRESULT(MK_E_EXCEEDEDDEADLINE)// 0x800401E1L
+ CASE_HRESULT(MK_E_NEEDGENERIC)// 0x800401E2L
+ CASE_HRESULT(MK_E_UNAVAILABLE)// 0x800401E3L
+ CASE_HRESULT(MK_E_SYNTAX)// 0x800401E4L
+ CASE_HRESULT(MK_E_NOOBJECT)// 0x800401E5L
+ CASE_HRESULT(MK_E_INVALIDEXTENSION)// 0x800401E6L
+ CASE_HRESULT(MK_E_INTERMEDIATEINTERFACENOTSUPPORTED)// 0x800401E7L
+ CASE_HRESULT(MK_E_NOTBINDABLE)// 0x800401E8L
+ CASE_HRESULT(MK_E_NOTBOUND)// 0x800401E9L
+ CASE_HRESULT(MK_E_CANTOPENFILE)// 0x800401EAL
+ CASE_HRESULT(MK_E_MUSTBOTHERUSER)// 0x800401EBL
+ CASE_HRESULT(MK_E_NOINVERSE)// 0x800401ECL
+ CASE_HRESULT(MK_E_NOSTORAGE)// 0x800401EDL
+ CASE_HRESULT(MK_E_NOPREFIX)// 0x800401EEL
+ CASE_HRESULT(MK_E_ENUMERATION_FAILED)// 0x800401EFL
+ CASE_HRESULT(CO_E_NOTINITIALIZED)// 0x800401F0L
+ CASE_HRESULT(CO_E_ALREADYINITIALIZED)// 0x800401F1L
+ CASE_HRESULT(CO_E_CANTDETERMINECLASS)// 0x800401F2L
+ CASE_HRESULT(CO_E_CLASSSTRING)// 0x800401F3L
+ CASE_HRESULT(CO_E_IIDSTRING)// 0x800401F4L
+ CASE_HRESULT(CO_E_APPNOTFOUND)// 0x800401F5L
+ CASE_HRESULT(CO_E_APPSINGLEUSE)// 0x800401F6L
+ CASE_HRESULT(CO_E_ERRORINAPP)// 0x800401F7L
+ CASE_HRESULT(CO_E_DLLNOTFOUND)// 0x800401F8L
+ CASE_HRESULT(CO_E_ERRORINDLL)// 0x800401F9L
+ CASE_HRESULT(CO_E_WRONGOSFORAPP)// 0x800401FAL
+ CASE_HRESULT(CO_E_OBJNOTREG)// 0x800401FBL
+ CASE_HRESULT(CO_E_OBJISREG)// 0x800401FCL
+ CASE_HRESULT(CO_E_OBJNOTCONNECTED)// 0x800401FDL
+ CASE_HRESULT(CO_E_APPDIDNTREG)// 0x800401FEL
+ CASE_HRESULT(CO_E_RELEASED)// 0x800401FFL
+
+ CASE_HRESULT(OLE_S_USEREG)// 0x00040000L
+ CASE_HRESULT(OLE_S_STATIC)// 0x00040001L
+ CASE_HRESULT(OLE_S_MAC_CLIPFORMAT)// 0x00040002L
+ CASE_HRESULT(DRAGDROP_S_DROP)// 0x00040100L
+ CASE_HRESULT(DRAGDROP_S_CANCEL)// 0x00040101L
+ CASE_HRESULT(DRAGDROP_S_USEDEFAULTCURSORS)// 0x00040102L
+ CASE_HRESULT(DATA_S_SAMEFORMATETC)// 0x00040130L
+ CASE_HRESULT(VIEW_S_ALREADY_FROZEN)// 0x00040140L
+ CASE_HRESULT(CACHE_S_FORMATETC_NOTSUPPORTED)// 0x00040170L
+ CASE_HRESULT(CACHE_S_SAMECACHE)// 0x00040171L
+ CASE_HRESULT(CACHE_S_SOMECACHES_NOTUPDATED)// 0x00040172L
+ CASE_HRESULT(OLEOBJ_S_INVALIDVERB)// 0x00040180L
+ CASE_HRESULT(OLEOBJ_S_CANNOT_DOVERB_NOW)// 0x00040181L
+ CASE_HRESULT(OLEOBJ_S_INVALIDHWND)// 0x00040182L
+ CASE_HRESULT(INPLACE_S_TRUNCATED)// 0x000401A0L
+ CASE_HRESULT(CONVERT10_S_NO_PRESENTATION)// 0x000401C0L
+ CASE_HRESULT(MK_S_REDUCED_TO_SELF)// 0x000401E2L
+ CASE_HRESULT(MK_S_ME)// 0x000401E4L
+ CASE_HRESULT(MK_S_HIM)// 0x000401E5L
+ CASE_HRESULT(MK_S_US)// 0x000401E6L
+ CASE_HRESULT(MK_S_MONIKERALREADYREGISTERED)// 0x000401E7L
+
+// ******************
+// FACILITY_WINDOWS
+// ******************
+
+ CASE_HRESULT(CO_E_CLASS_CREATE_FAILED)// 0x80080001L
+ CASE_HRESULT(CO_E_SCM_ERROR)// 0x80080002L
+ CASE_HRESULT(CO_E_SCM_RPC_FAILURE)// 0x80080003L
+ CASE_HRESULT(CO_E_BAD_PATH)// 0x80080004L
+ CASE_HRESULT(CO_E_SERVER_EXEC_FAILURE)// 0x80080005L
+ CASE_HRESULT(CO_E_OBJSRV_RPC_FAILURE)// 0x80080006L
+ CASE_HRESULT(MK_E_NO_NORMALIZED)// 0x80080007L
+ CASE_HRESULT(CO_E_SERVER_STOPPING)// 0x80080008L
+ CASE_HRESULT(MEM_E_INVALID_ROOT)// 0x80080009L
+ CASE_HRESULT(MEM_E_INVALID_LINK)// 0x80080010L
+ CASE_HRESULT(MEM_E_INVALID_SIZE)// 0x80080011L
+
+// ******************
+// FACILITY_DISPATCH
+// ******************
+
+ CASE_HRESULT(DISP_E_UNKNOWNINTERFACE)// 0x80020001L
+ CASE_HRESULT(DISP_E_MEMBERNOTFOUND)// 0x80020003L
+ CASE_HRESULT(DISP_E_PARAMNOTFOUND)// 0x80020004L
+ CASE_HRESULT(DISP_E_TYPEMISMATCH)// 0x80020005L
+ CASE_HRESULT(DISP_E_UNKNOWNNAME)// 0x80020006L
+ CASE_HRESULT(DISP_E_NONAMEDARGS)// 0x80020007L
+ CASE_HRESULT(DISP_E_BADVARTYPE)// 0x80020008L
+ CASE_HRESULT(DISP_E_EXCEPTION)// 0x80020009L
+ CASE_HRESULT(DISP_E_OVERFLOW)// 0x8002000AL
+ CASE_HRESULT(DISP_E_BADINDEX)// 0x8002000BL
+ CASE_HRESULT(DISP_E_UNKNOWNLCID)// 0x8002000CL
+ CASE_HRESULT(DISP_E_ARRAYISLOCKED)// 0x8002000DL
+ CASE_HRESULT(DISP_E_BADPARAMCOUNT)// 0x8002000EL
+ CASE_HRESULT(DISP_E_PARAMNOTOPTIONAL)// 0x8002000FL
+ CASE_HRESULT(DISP_E_BADCALLEE)// 0x80020010L
+ CASE_HRESULT(DISP_E_NOTACOLLECTION)// 0x80020011L
+ CASE_HRESULT(TYPE_E_BUFFERTOOSMALL)// 0x80028016L
+ CASE_HRESULT(TYPE_E_INVDATAREAD)// 0x80028018L
+ CASE_HRESULT(TYPE_E_UNSUPFORMAT)// 0x80028019L
+ CASE_HRESULT(TYPE_E_REGISTRYACCESS)// 0x8002801CL
+ CASE_HRESULT(TYPE_E_LIBNOTREGISTERED)// 0x8002801DL
+ CASE_HRESULT(TYPE_E_UNDEFINEDTYPE)// 0x80028027L
+ CASE_HRESULT(TYPE_E_QUALIFIEDNAMEDISALLOWED)// 0x80028028L
+ CASE_HRESULT(TYPE_E_INVALIDSTATE)// 0x80028029L
+ CASE_HRESULT(TYPE_E_WRONGTYPEKIND)// 0x8002802AL
+ CASE_HRESULT(TYPE_E_ELEMENTNOTFOUND)// 0x8002802BL
+ CASE_HRESULT(TYPE_E_AMBIGUOUSNAME)// 0x8002802CL
+ CASE_HRESULT(TYPE_E_NAMECONFLICT)// 0x8002802DL
+ CASE_HRESULT(TYPE_E_UNKNOWNLCID)// 0x8002802EL
+ CASE_HRESULT(TYPE_E_DLLFUNCTIONNOTFOUND)// 0x8002802FL
+ CASE_HRESULT(TYPE_E_BADMODULEKIND)// 0x800288BDL
+ CASE_HRESULT(TYPE_E_SIZETOOBIG)// 0x800288C5L
+ CASE_HRESULT(TYPE_E_DUPLICATEID)// 0x800288C6L
+ CASE_HRESULT(TYPE_E_INVALIDID)// 0x800288CFL
+ CASE_HRESULT(TYPE_E_TYPEMISMATCH)// 0x80028CA0L
+ CASE_HRESULT(TYPE_E_OUTOFBOUNDS)// 0x80028CA1L
+ CASE_HRESULT(TYPE_E_IOERROR)// 0x80028CA2L
+ CASE_HRESULT(TYPE_E_CANTCREATETMPFILE)// 0x80028CA3L
+ CASE_HRESULT(TYPE_E_CANTLOADLIBRARY)// 0x80029C4AL
+ CASE_HRESULT(TYPE_E_INCONSISTENTPROPFUNCS)// 0x80029C83L
+ CASE_HRESULT(TYPE_E_CIRCULARTYPE)// 0x80029C84L
+
+// ******************
+// FACILITY_STORAGE
+// ******************
+
+ CASE_HRESULT(STG_E_INVALIDFUNCTION)// 0x80030001L
+ CASE_HRESULT(STG_E_FILENOTFOUND)// 0x80030002L
+ CASE_HRESULT(STG_E_PATHNOTFOUND)// 0x80030003L
+ CASE_HRESULT(STG_E_TOOMANYOPENFILES)// 0x80030004L
+ CASE_HRESULT(STG_E_ACCESSDENIED)// 0x80030005L
+ CASE_HRESULT(STG_E_INVALIDHANDLE)// 0x80030006L
+ CASE_HRESULT(STG_E_INSUFFICIENTMEMORY)// 0x80030008L
+ CASE_HRESULT(STG_E_INVALIDPOINTER)// 0x80030009L
+ CASE_HRESULT(STG_E_NOMOREFILES)// 0x80030012L
+ CASE_HRESULT(STG_E_DISKISWRITEPROTECTED)// 0x80030013L
+ CASE_HRESULT(STG_E_SEEKERROR)// 0x80030019L
+ CASE_HRESULT(STG_E_WRITEFAULT)// 0x8003001DL
+ CASE_HRESULT(STG_E_READFAULT)// 0x8003001EL
+ CASE_HRESULT(STG_E_SHAREVIOLATION)// 0x80030020L
+ CASE_HRESULT(STG_E_LOCKVIOLATION)// 0x80030021L
+ CASE_HRESULT(STG_E_FILEALREADYEXISTS)// 0x80030050L
+ CASE_HRESULT(STG_E_INVALIDPARAMETER)// 0x80030057L
+ CASE_HRESULT(STG_E_MEDIUMFULL)// 0x80030070L
+ CASE_HRESULT(STG_E_ABNORMALAPIEXIT)// 0x800300FAL
+ CASE_HRESULT(STG_E_INVALIDHEADER)// 0x800300FBL
+ CASE_HRESULT(STG_E_INVALIDNAME)// 0x800300FCL
+ CASE_HRESULT(STG_E_UNKNOWN)// 0x800300FDL
+ CASE_HRESULT(STG_E_UNIMPLEMENTEDFUNCTION)// 0x800300FEL
+ CASE_HRESULT(STG_E_INVALIDFLAG)// 0x800300FFL
+ CASE_HRESULT(STG_E_INUSE)// 0x80030100L
+ CASE_HRESULT(STG_E_NOTCURRENT)// 0x80030101L
+ CASE_HRESULT(STG_E_REVERTED)// 0x80030102L
+ CASE_HRESULT(STG_E_CANTSAVE)// 0x80030103L
+ CASE_HRESULT(STG_E_OLDFORMAT)// 0x80030104L
+ CASE_HRESULT(STG_E_OLDDLL)// 0x80030105L
+ CASE_HRESULT(STG_E_SHAREREQUIRED)// 0x80030106L
+ CASE_HRESULT(STG_E_NOTFILEBASEDSTORAGE)// 0x80030107L
+ CASE_HRESULT(STG_S_CONVERTED)// 0x00030200L
+
+// ******************
+// FACILITY_RPC
+// ******************
+
+ CASE_HRESULT(RPC_E_CALL_REJECTED)// 0x80010001L
+ CASE_HRESULT(RPC_E_CALL_CANCELED)// 0x80010002L
+ CASE_HRESULT(RPC_E_CANTPOST_INSENDCALL)// 0x80010003L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_INASYNCCALL)// 0x80010004L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_INEXTERNALCALL)// 0x80010005L
+ CASE_HRESULT(RPC_E_CONNECTION_TERMINATED)// 0x80010006L
+ CASE_HRESULT(RPC_E_SERVER_DIED)// 0x80010007L
+ CASE_HRESULT(RPC_E_CLIENT_DIED)// 0x80010008L
+ CASE_HRESULT(RPC_E_INVALID_DATAPACKET)// 0x80010009L
+ CASE_HRESULT(RPC_E_CANTTRANSMIT_CALL)// 0x8001000AL
+ CASE_HRESULT(RPC_E_CLIENT_CANTMARSHAL_DATA)// 0x8001000BL
+ CASE_HRESULT(RPC_E_CLIENT_CANTUNMARSHAL_DATA)// 0x8001000CL
+ CASE_HRESULT(RPC_E_SERVER_CANTMARSHAL_DATA)// 0x8001000DL
+ CASE_HRESULT(RPC_E_SERVER_CANTUNMARSHAL_DATA)// 0x8001000EL
+ CASE_HRESULT(RPC_E_INVALID_DATA)// 0x8001000FL
+ CASE_HRESULT(RPC_E_INVALID_PARAMETER)// 0x80010010L
+ CASE_HRESULT(RPC_E_CANTCALLOUT_AGAIN)// 0x80010011L
+ CASE_HRESULT(RPC_E_SERVER_DIED_DNE)// 0x80010012L
+ CASE_HRESULT(RPC_E_SYS_CALL_FAILED)// 0x80010100L
+ CASE_HRESULT(RPC_E_OUT_OF_RESOURCES)// 0x80010101L
+ CASE_HRESULT(RPC_E_ATTEMPTED_MULTITHREAD)// 0x80010102L
+ CASE_HRESULT(RPC_E_NOT_REGISTERED)// 0x80010103L
+ CASE_HRESULT(RPC_E_FAULT)// 0x80010104L
+ CASE_HRESULT(RPC_E_SERVERFAULT)// 0x80010105L
+ CASE_HRESULT(RPC_E_CHANGED_MODE)// 0x80010106L
+ CASE_HRESULT(RPC_E_INVALIDMETHOD)// 0x80010107L
+ CASE_HRESULT(RPC_E_DISCONNECTED)// 0x80010108L
+ CASE_HRESULT(RPC_E_RETRY)// 0x80010109L
+ CASE_HRESULT(RPC_E_SERVERCALL_RETRYLATER)// 0x8001010AL
+ CASE_HRESULT(RPC_E_SERVERCALL_REJECTED)// 0x8001010BL
+ CASE_HRESULT(RPC_E_INVALID_CALLDATA)// 0x8001010CL
+ CASE_HRESULT(RPC_E_CANTCALLOUT_ININPUTSYNCCALL)// 0x8001010DL
+ CASE_HRESULT(RPC_E_WRONG_THREAD)// 0x8001010EL
+ CASE_HRESULT(RPC_E_THREAD_NOT_INIT)// 0x8001010FL
+ CASE_HRESULT(RPC_E_UNEXPECTED)// 0x8001FFFFL
+
+// ******************
+// FACILITY_CTL
+// ******************
+
+ CASE_HRESULT(CTL_E_ILLEGALFUNCTIONCALL)
+ CASE_HRESULT(CTL_E_OVERFLOW)
+ CASE_HRESULT(CTL_E_OUTOFMEMORY)
+ CASE_HRESULT(CTL_E_DIVISIONBYZERO)
+ CASE_HRESULT(CTL_E_OUTOFSTRINGSPACE)
+ CASE_HRESULT(CTL_E_OUTOFSTACKSPACE)
+ CASE_HRESULT(CTL_E_BADFILENAMEORNUMBER)
+ CASE_HRESULT(CTL_E_FILENOTFOUND)
+ CASE_HRESULT(CTL_E_BADFILEMODE)
+ CASE_HRESULT(CTL_E_FILEALREADYOPEN)
+ CASE_HRESULT(CTL_E_DEVICEIOERROR)
+ CASE_HRESULT(CTL_E_FILEALREADYEXISTS)
+ CASE_HRESULT(CTL_E_BADRECORDLENGTH)
+ CASE_HRESULT(CTL_E_DISKFULL)
+ CASE_HRESULT(CTL_E_BADRECORDNUMBER)
+ CASE_HRESULT(CTL_E_BADFILENAME)
+ CASE_HRESULT(CTL_E_TOOMANYFILES)
+ CASE_HRESULT(CTL_E_DEVICEUNAVAILABLE)
+ CASE_HRESULT(CTL_E_PERMISSIONDENIED)
+ CASE_HRESULT(CTL_E_DISKNOTREADY)
+ CASE_HRESULT(CTL_E_PATHFILEACCESSERROR)
+ CASE_HRESULT(CTL_E_PATHNOTFOUND)
+ CASE_HRESULT(CTL_E_INVALIDPATTERNSTRING)
+ CASE_HRESULT(CTL_E_INVALIDUSEOFNULL)
+ CASE_HRESULT(CTL_E_INVALIDFILEFORMAT)
+ CASE_HRESULT(CTL_E_INVALIDPROPERTYVALUE)
+ CASE_HRESULT(CTL_E_INVALIDPROPERTYARRAYINDEX)
+ CASE_HRESULT(CTL_E_SETNOTSUPPORTEDATRUNTIME)
+ CASE_HRESULT(CTL_E_SETNOTSUPPORTED)
+ CASE_HRESULT(CTL_E_NEEDPROPERTYARRAYINDEX)
+ CASE_HRESULT(CTL_E_SETNOTPERMITTED)
+ CASE_HRESULT(CTL_E_GETNOTSUPPORTEDATRUNTIME)
+ CASE_HRESULT(CTL_E_GETNOTSUPPORTED)
+ CASE_HRESULT(CTL_E_PROPERTYNOTFOUND)
+ CASE_HRESULT(CTL_E_INVALIDCLIPBOARDFORMAT)
+ CASE_HRESULT(CTL_E_INVALIDPICTURE)
+ CASE_HRESULT(CTL_E_PRINTERERROR)
+ CASE_HRESULT(CTL_E_CANTSAVEFILETOTEMP)
+ CASE_HRESULT(CTL_E_SEARCHTEXTNOTFOUND)
+ CASE_HRESULT(CTL_E_REPLACEMENTSTOOLONG)
+#endif // FEATURE_COMINTEROP
+
+#ifdef _DEBUG // @todo: do we want to burn strings for this in a free build?
+
+ CASE_HRESULT(CEE_E_CVTRES_NOT_FOUND)
+ CASE_HRESULT(COR_E_APPDOMAINUNLOADED)
+ CASE_HRESULT(COR_E_CANNOTUNLOADAPPDOMAIN)
+ CASE_HRESULT(MSEE_E_ASSEMBLYLOADINPROGRESS)
+ CASE_HRESULT(COR_E_FIXUPSINEXE)
+ CASE_HRESULT(COR_E_MODULE_HASH_CHECK_FAILED)
+ CASE_HRESULT(FUSION_E_LOADFROM_BLOCKED)
+ CASE_HRESULT(FUSION_E_CACHEFILE_FAILED)
+ CASE_HRESULT(FUSION_E_REF_DEF_MISMATCH)
+ CASE_HRESULT(FUSION_E_INVALID_PRIVATE_ASM_LOCATION)
+ CASE_HRESULT(FUSION_E_ASM_MODULE_MISSING)
+ CASE_HRESULT(FUSION_E_PRIVATE_ASM_DISALLOWED)
+ CASE_HRESULT(FUSION_E_SIGNATURE_CHECK_FAILED)
+ CASE_HRESULT(FUSION_E_INVALID_NAME)
+ CASE_HRESULT(FUSION_E_CODE_DOWNLOAD_DISABLED)
+ CASE_HRESULT(CLDB_E_FILE_BADREAD)
+ CASE_HRESULT(CLDB_E_FILE_BADWRITE)
+ CASE_HRESULT(CLDB_S_TRUNCATION)
+ CASE_HRESULT(CLDB_E_FILE_OLDVER)
+ CASE_HRESULT(CLDB_E_SMDUPLICATE)
+ CASE_HRESULT(CLDB_E_NO_DATA)
+ CASE_HRESULT(CLDB_E_INCOMPATIBLE)
+ CASE_HRESULT(CLDB_E_FILE_CORRUPT)
+ CASE_HRESULT(CLDB_E_BADUPDATEMODE)
+ CASE_HRESULT(CLDB_E_INDEX_NOTFOUND)
+ CASE_HRESULT(CLDB_E_RECORD_NOTFOUND)
+ CASE_HRESULT(CLDB_E_RECORD_OUTOFORDER)
+ CASE_HRESULT(CLDB_E_TOO_BIG)
+ CASE_HRESULT(META_E_BADMETADATA)
+ CASE_HRESULT(META_E_BAD_SIGNATURE)
+ CASE_HRESULT(META_E_BAD_INPUT_PARAMETER)
+ CASE_HRESULT(META_E_CANNOTRESOLVETYPEREF)
+ CASE_HRESULT(META_S_DUPLICATE)
+ CASE_HRESULT(META_E_STRINGSPACE_FULL)
+ CASE_HRESULT(META_E_HAS_UNMARKALL)
+ CASE_HRESULT(META_E_MUST_CALL_UNMARKALL)
+ CASE_HRESULT(META_E_CA_INVALID_TARGET)
+ CASE_HRESULT(META_E_CA_INVALID_VALUE)
+ CASE_HRESULT(META_E_CA_INVALID_BLOB)
+ CASE_HRESULT(META_E_CA_REPEATED_ARG)
+ CASE_HRESULT(META_E_CA_UNKNOWN_ARGUMENT)
+ CASE_HRESULT(META_E_CA_UNEXPECTED_TYPE)
+ CASE_HRESULT(META_E_CA_INVALID_ARGTYPE)
+ CASE_HRESULT(META_E_CA_INVALID_ARG_FOR_TYPE)
+ CASE_HRESULT(META_E_CA_INVALID_UUID)
+ CASE_HRESULT(META_E_CA_INVALID_MARSHALAS_FIELDS)
+ CASE_HRESULT(META_E_CA_NT_FIELDONLY)
+ CASE_HRESULT(META_E_CA_NEGATIVE_PARAMINDEX)
+ CASE_HRESULT(META_E_CA_NEGATIVE_CONSTSIZE)
+ CASE_HRESULT(META_E_CA_FIXEDSTR_SIZE_REQUIRED)
+ CASE_HRESULT(META_E_CA_CUSTMARSH_TYPE_REQUIRED)
+ CASE_HRESULT(META_E_CA_BAD_FRIENDS_ARGS)
+ CASE_HRESULT(VLDTR_E_RID_OUTOFRANGE)
+ CASE_HRESULT(VLDTR_E_STRING_INVALID)
+ CASE_HRESULT(VLDTR_E_GUID_INVALID)
+ CASE_HRESULT(VLDTR_E_BLOB_INVALID)
+ CASE_HRESULT(VLDTR_E_MR_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_SIGNULL)
+ CASE_HRESULT(VLDTR_E_MD_BADCALLINGCONV)
+ CASE_HRESULT(VLDTR_E_MD_THISSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_NOTTHISNOTSTATIC)
+ CASE_HRESULT(VLDTR_E_MD_NOARGCNT)
+ CASE_HRESULT(VLDTR_E_SIG_MISSELTYPE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSTKN)
+ CASE_HRESULT(VLDTR_E_SIG_TKNBAD)
+ CASE_HRESULT(VLDTR_E_SIG_MISSFPTR)
+ CASE_HRESULT(VLDTR_E_SIG_MISSFPTRARGCNT)
+ CASE_HRESULT(VLDTR_E_SIG_MISSRANK)
+ CASE_HRESULT(VLDTR_E_SIG_MISSNSIZE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSSIZE)
+ CASE_HRESULT(VLDTR_E_SIG_MISSNLBND)
+ CASE_HRESULT(VLDTR_E_SIG_MISSLBND)
+ CASE_HRESULT(VLDTR_E_SIG_BADELTYPE)
+ CASE_HRESULT(VLDTR_E_TD_ENCLNOTNESTED)
+ CASE_HRESULT(VLDTR_E_FMD_PINVOKENOTSTATIC)
+ CASE_HRESULT(VLDTR_E_SIG_SENTINMETHODDEF)
+ CASE_HRESULT(VLDTR_E_SIG_SENTMUSTVARARG)
+ CASE_HRESULT(VLDTR_E_SIG_MULTSENTINELS)
+ CASE_HRESULT(VLDTR_E_SIG_MISSARG)
+ CASE_HRESULT(VLDTR_E_SIG_BYREFINFIELD)
+ CASE_HRESULT(VLDTR_E_SIG_BADVOID)
+ CASE_HRESULT(CORDBG_E_UNRECOVERABLE_ERROR)
+ CASE_HRESULT(CORDBG_E_PROCESS_TERMINATED)
+ CASE_HRESULT(CORDBG_E_PROCESS_NOT_SYNCHRONIZED)
+ CASE_HRESULT(CORDBG_E_CLASS_NOT_LOADED)
+ CASE_HRESULT(CORDBG_E_IL_VAR_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_BAD_REFERENCE_VALUE)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_NON_NATIVE_FRAME)
+ CASE_HRESULT(CORDBG_E_CODE_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_FUNCTION_NOT_IL)
+ CASE_HRESULT(CORDBG_S_BAD_START_SEQUENCE_POINT)
+ CASE_HRESULT(CORDBG_S_BAD_END_SEQUENCE_POINT)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_FINALLY)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_OUT_OF_FINALLY)
+ CASE_HRESULT(CORDBG_E_CANT_SET_IP_INTO_CATCH)
+ CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_NONLEAF_FRAME)
+ CASE_HRESULT(CORDBG_E_SET_IP_IMPOSSIBLE)
+ CASE_HRESULT(CORDBG_E_FUNC_EVAL_BAD_START_POINT)
+ CASE_HRESULT(CORDBG_E_INVALID_OBJECT)
+ CASE_HRESULT(CORDBG_E_FUNC_EVAL_NOT_COMPLETE)
+ CASE_HRESULT(CORDBG_S_FUNC_EVAL_HAS_NO_RESULT)
+ CASE_HRESULT(CORDBG_S_VALUE_POINTS_TO_VOID)
+ CASE_HRESULT(CORDBG_S_FUNC_EVAL_ABORTED)
+ CASE_HRESULT(CORDBG_E_STATIC_VAR_NOT_AVAILABLE)
+ CASE_HRESULT(CORDBG_E_CANT_SETIP_INTO_OR_OUT_OF_FILTER)
+ CASE_HRESULT(CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
+ CASE_HRESULT(CORDBG_E_CANT_SET_TO_JMC)
+ CASE_HRESULT(CORDBG_E_BAD_THREAD_STATE)
+ CASE_HRESULT(CORDBG_E_DEBUGGER_ALREADY_ATTACHED)
+ CASE_HRESULT(CORDBG_E_SUPERFLOUS_CONTINUE)
+ CASE_HRESULT(CORDBG_E_SET_VALUE_NOT_ALLOWED_ON_NONLEAF_FRAME)
+ CASE_HRESULT(CORDBG_E_ENC_MODULE_NOT_ENC_ENABLED)
+ CASE_HRESULT(CORDBG_E_SET_IP_NOT_ALLOWED_ON_EXCEPTION)
+ CASE_HRESULT(CORDBG_E_VARIABLE_IS_ACTUALLY_LITERAL)
+ CASE_HRESULT(CORDBG_E_PROCESS_DETACHED)
+ CASE_HRESULT(CORDBG_E_ENC_CANT_ADD_FIELD_TO_VALUE_OR_LAYOUT_CLASS)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_STATIC)
+ CASE_HRESULT(CORDBG_E_FIELD_NOT_INSTANCE)
+ CASE_HRESULT(CORDBG_E_ENC_JIT_CANT_UPDATE)
+ CASE_HRESULT(CORDBG_E_ENC_INTERNAL_ERROR)
+ CASE_HRESULT(CORDBG_E_ENC_HANGING_FIELD)
+ CASE_HRESULT(CORDBG_E_MODULE_NOT_LOADED)
+ CASE_HRESULT(CORDBG_E_UNABLE_TO_SET_BREAKPOINT)
+ CASE_HRESULT(CORDBG_E_DEBUGGING_NOT_POSSIBLE)
+ CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_ENABLED)
+ CASE_HRESULT(CORDBG_E_KERNEL_DEBUGGER_PRESENT)
+ CASE_HRESULT(CORDBG_E_INCOMPATIBLE_PROTOCOL)
+ CASE_HRESULT(CORDBG_E_TOO_MANY_PROCESSES)
+ CASE_HRESULT(CORDBG_E_INTEROP_NOT_SUPPORTED)
+ CASE_HRESULT(CORDBG_E_NO_REMAP_BREAKPIONT)
+ CASE_HRESULT(CORDBG_E_OBJECT_NEUTERED)
+ CASE_HRESULT(CORPROF_E_FUNCTION_NOT_COMPILED)
+ CASE_HRESULT(CORPROF_E_DATAINCOMPLETE)
+ CASE_HRESULT(CORPROF_E_FUNCTION_NOT_IL)
+ CASE_HRESULT(CORPROF_E_NOT_MANAGED_THREAD)
+ CASE_HRESULT(CORPROF_E_CALL_ONLY_FROM_INIT)
+ CASE_HRESULT(CORPROF_E_NOT_YET_AVAILABLE)
+ CASE_HRESULT(SECURITY_E_INCOMPATIBLE_SHARE)
+ CASE_HRESULT(SECURITY_E_UNVERIFIABLE)
+ CASE_HRESULT(SECURITY_E_INCOMPATIBLE_EVIDENCE)
+ CASE_HRESULT(CLDB_E_INTERNALERROR)
+ CASE_HRESULT(CORSEC_E_POLICY_EXCEPTION)
+ CASE_HRESULT(CORSEC_E_MIN_GRANT_FAIL)
+ CASE_HRESULT(CORSEC_E_NO_EXEC_PERM)
+ CASE_HRESULT(CORSEC_E_XMLSYNTAX)
+ CASE_HRESULT(CORSEC_E_INVALID_STRONGNAME)
+ CASE_HRESULT(CORSEC_E_MISSING_STRONGNAME)
+ CASE_HRESULT(CORSEC_E_INVALID_IMAGE_FORMAT)
+ CASE_HRESULT(CORSEC_E_CRYPTO)
+ CASE_HRESULT(CORSEC_E_CRYPTO_UNEX_OPER)
+ CASE_HRESULT(CORSECATTR_E_BAD_ACTION)
+ CASE_HRESULT(COR_E_APPLICATION)
+ CASE_HRESULT(COR_E_ARGUMENTOUTOFRANGE)
+ CASE_HRESULT(COR_E_ARITHMETIC)
+ CASE_HRESULT(COR_E_ARRAYTYPEMISMATCH)
+ CASE_HRESULT(COR_E_CONTEXTMARSHAL)
+ CASE_HRESULT(COR_E_TIMEOUT)
+ CASE_HRESULT(COR_E_DIVIDEBYZERO)
+ CASE_HRESULT(COR_E_EXCEPTION)
+ CASE_HRESULT(COR_E_EXECUTIONENGINE)
+ CASE_HRESULT(COR_E_FIELDACCESS)
+ CASE_HRESULT(COR_E_FORMAT)
+ CASE_HRESULT(COR_E_BADIMAGEFORMAT)
+ CASE_HRESULT(COR_E_ASSEMBLYEXPECTED)
+ CASE_HRESULT(COR_E_TYPEUNLOADED)
+ CASE_HRESULT(COR_E_INDEXOUTOFRANGE)
+ CASE_HRESULT(COR_E_INVALIDOPERATION)
+ CASE_HRESULT(COR_E_INVALIDPROGRAM)
+ CASE_HRESULT(COR_E_MEMBERACCESS)
+ CASE_HRESULT(COR_E_METHODACCESS)
+ CASE_HRESULT(COR_E_MISSINGFIELD)
+ CASE_HRESULT(COR_E_MISSINGMANIFESTRESOURCE)
+ CASE_HRESULT(COR_E_MISSINGMEMBER)
+ CASE_HRESULT(COR_E_MISSINGMETHOD)
+ CASE_HRESULT(COR_E_MULTICASTNOTSUPPORTED)
+ CASE_HRESULT(COR_E_NOTFINITENUMBER)
+ CASE_HRESULT(COR_E_DUPLICATEWAITOBJECT)
+ CASE_HRESULT(COR_E_PLATFORMNOTSUPPORTED)
+ CASE_HRESULT(COR_E_NOTSUPPORTED)
+ CASE_HRESULT(COR_E_OVERFLOW)
+ CASE_HRESULT(COR_E_RANK)
+ CASE_HRESULT(COR_E_SECURITY)
+ CASE_HRESULT(COR_E_SERIALIZATION)
+ CASE_HRESULT(COR_E_STACKOVERFLOW)
+ CASE_HRESULT(COR_E_SYNCHRONIZATIONLOCK)
+ CASE_HRESULT(COR_E_SYSTEM)
+ CASE_HRESULT(COR_E_THREADABORTED)
+ CASE_HRESULT(COR_E_THREADINTERRUPTED)
+ CASE_HRESULT(COR_E_THREADSTATE)
+ CASE_HRESULT(COR_E_THREADSTOP)
+ CASE_HRESULT(COR_E_TYPEINITIALIZATION)
+ CASE_HRESULT(COR_E_TYPELOAD)
+ CASE_HRESULT(COR_E_ENTRYPOINTNOTFOUND)
+ CASE_HRESULT(COR_E_DLLNOTFOUND)
+ CASE_HRESULT(COR_E_VERIFICATION)
+ CASE_HRESULT(COR_E_INVALIDCOMOBJECT)
+ CASE_HRESULT(COR_E_MARSHALDIRECTIVE)
+ CASE_HRESULT(COR_E_INVALIDOLEVARIANTTYPE)
+ CASE_HRESULT(COR_E_SAFEARRAYTYPEMISMATCH)
+ CASE_HRESULT(COR_E_SAFEARRAYRANKMISMATCH)
+ CASE_HRESULT(COR_E_INVALIDFILTERCRITERIA)
+ CASE_HRESULT(COR_E_REFLECTIONTYPELOAD)
+ CASE_HRESULT(COR_E_TARGET)
+ CASE_HRESULT(COR_E_TARGETINVOCATION)
+ CASE_HRESULT(COR_E_CUSTOMATTRIBUTEFORMAT)
+ CASE_HRESULT(COR_E_ENDOFSTREAM)
+ CASE_HRESULT(COR_E_FILELOAD)
+ CASE_HRESULT(COR_E_FILENOTFOUND)
+ CASE_HRESULT(COR_E_IO)
+ CASE_HRESULT(COR_E_DIRECTORYNOTFOUND)
+ CASE_HRESULT(COR_E_PATHTOOLONG)
+ CASE_HRESULT(COR_E_OBJECTDISPOSED)
+ CASE_HRESULT(COR_E_NEWER_RUNTIME)
+ CASE_HRESULT(CLR_E_SHIM_RUNTIMELOAD)
+ CASE_HRESULT(VER_E_FIELD_SIG)
+ CASE_HRESULT(CORDBG_E_THREAD_NOT_SCHEDULED)
+#endif
+
+ default:
+ return NULL;
+ }
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
+// ---------------------------------------------------------------------------
+// HRException class. Implements exception API for exceptions from HRESULTS
+// ---------------------------------------------------------------------------
+
+HRESULT HRException::GetHR()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+ return m_hr;
+}
+
+// ---------------------------------------------------------------------------
+// COMException class. - moved to COMEx.cpp
+// ---------------------------------------------------------------------------
+
+// ---------------------------------------------------------------------------
+// SEHException class. Implements exception API for SEH exception info
+// ---------------------------------------------------------------------------
+
+HRESULT SEHException::GetHR()
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ if (IsComPlusException(&m_exception)) // EE exception
+ return (HRESULT) m_exception.ExceptionInformation[0];
+ else
+ return m_exception.ExceptionCode;
+}
+
+IErrorInfo *SEHException::GetErrorInfo()
+{
+ LIMITED_METHOD_CONTRACT;
+ return NULL;
+}
+
+void SEHException::GetMessage(SString &string)
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (IsComPlusException(&m_exception)) // EE exception
+ {
+ GenerateTopLevelHRExceptionMessage(GetHR(), string);
+ }
+ else
+ {
+ if (m_exception.ExceptionCode != 0)
+ {
+ string.Printf("Exception code 0x%.8x", m_exception.ExceptionCode);
+ }
+ else
+ {
+ // If we don't have a valid exception code, then give a generic message that's a little nicer than
+ // "code 0x00000000".
+ string.Printf("Unknown exception");
+ }
+ }
+}
+
+//==============================================================================
+// DelegatingException class. Implements exception API for "foreign" exceptions.
+//==============================================================================
+
+DelegatingException::DelegatingException()
+ : m_delegatedException((Exception*)DELEGATE_NOT_YET_SET)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+} // DelegatingException::DelegatingException()
+
+//------------------------------------------------------------------------------
+DelegatingException::~DelegatingException()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If there is a valid delegate pointer (inited and non-NULL), delete it.
+ if (IsDelegateValid())
+ Delete(m_delegatedException);
+
+ // Avoid confusion.
+ m_delegatedException = NULL;
+} // DelegatingException::~DelegatingException()
+
+//------------------------------------------------------------------------------
+// Retrieve the delegating exception, or get one from the Thread, or get NULL.
+Exception* DelegatingException::GetDelegate()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // If we haven't gotten the exception pointer before..
+ if (!IsDelegateSet())
+ {
+ // .. get it now. NULL in case there isn't one and we take default action.
+ m_delegatedException = NULL;
+ GetLastThrownObjectExceptionFromThread(&m_delegatedException);
+ }
+
+ return m_delegatedException;
+} // Exception* DelegatingException::GetDelegate()
+
+//------------------------------------------------------------------------------
+// Virtual overrides
+HRESULT DelegatingException::GetHR()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to E_FAIL.
+ return pDelegate ? pDelegate->GetHR() : E_FAIL;
+
+} // HRESULT DelegatingException::GetHR()
+
+//------------------------------------------------------------------------------
+IErrorInfo *DelegatingException::GetErrorInfo()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to NULL.
+ return pDelegate ? pDelegate->GetErrorInfo() : NULL;
+
+} // IErrorInfo *DelegatingException::GetErrorInfo()
+
+//------------------------------------------------------------------------------
+void DelegatingException::GetMessage(SString &result)
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Retrieve any delegating exception.
+ Exception *pDelegate = GetDelegate();
+
+ // If there is a delegate exception, defer to it. Otherwise,
+ // default to a generic message.
+ if (pDelegate)
+ {
+ pDelegate->GetMessage(result);
+ }
+ else
+ {
+ // If we don't have a valid exception code, then give a generic message
+ // that's a little nicer than "code 0x00000000".
+ result.Printf("Unknown exception");
+ }
+} // void DelegatingException::GetMessage()
+
+//------------------------------------------------------------------------------
+Exception *DelegatingException::Clone()
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Clone the base exception, this will also take care of cloning the inner
+ // exception if there is one.
+ NewHolder<DelegatingException> retExcep((DelegatingException*)Exception::Clone());
+
+ // If there is a valid delegating exception...
+ if (IsDelegateValid())
+ { // ... clone it.
+ retExcep->m_delegatedException = m_delegatedException->Clone();
+ }
+ else
+ { // ... but if there is not, just copy -- either NULL or DELEGATE_NOT_YET_SET
+ retExcep->m_delegatedException = m_delegatedException;
+ }
+
+ retExcep.SuppressRelease();
+ return retExcep;
+} // virtual Exception *DelegatingException::Clone()
+
+//==============================================================================
+//==============================================================================
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr)
+{
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr);
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ EX_THROW(HRException, (hr));
+}
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr, SString const &msg)
+{
+ WRAPPER_NO_CONTRACT;
+
+ STRESS_LOG1(LF_EH, LL_INFO100, "ThrowHR: HR = %x\n", hr);
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ EX_THROW(HRMsgException, (hr, msg));
+}
+
+void DECLSPEC_NORETURN ThrowHR(HRESULT hr, UINT uText)
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC_HOST_ONLY;
+
+ if (hr == E_OUTOFMEMORY)
+ ThrowOutOfMemory();
+
+ // Catchers assume only failing hresults
+ _ASSERTE(FAILED(hr));
+ if (hr == S_OK)
+ hr = E_FAIL;
+
+ SString sExceptionText;
+
+ // We won't check the return value here. If it fails, we'll just
+ // throw the HR
+ sExceptionText.LoadResource(CCompRC::Error, uText);
+
+ EX_THROW(HRMsgException, (hr, sExceptionText));
+}
+
+void DECLSPEC_NORETURN ThrowWin32(DWORD err)
+{
+ WRAPPER_NO_CONTRACT;
+ if (err == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ {
+ ThrowHR(HRESULT_FROM_WIN32(err));
+ }
+}
+
+void DECLSPEC_NORETURN ThrowLastError()
+{
+ WRAPPER_NO_CONTRACT;
+ SUPPORTS_DAC;
+
+ ThrowWin32(GetLastError());
+}
+
+void DECLSPEC_NORETURN ThrowOutOfMemory()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+#ifndef DACCESS_COMPILE
+
+ // Use volatile store to prevent compiler from optimizing the static variable away
+ VolatileStoreWithoutBarrier<HRESULT>(&g_hrFatalError, COR_E_OUTOFMEMORY);
+
+ // Regular CLR builds - throw our pre-created OOM exception object
+ PAL_CPP_THROW(Exception *, Exception::GetOOMException());
+
+#else
+
+ // DAC builds - raise a DacError
+ DacError(E_OUTOFMEMORY);
+
+ // DacError always throws but isn't marked DECLSPEC_NORETURN so we have to
+ // tell the compiler that this code is unreachable. We could mark DacError
+ // (and DacNotImpl) as DECLSPEC_NORETURN, but then we've have to update a
+ // lot of code where we do something afterwards. Also, due to inlining,
+ // we'd sometimes have to change functions which call functions that only
+ // call DacNotImpl. I have these changes in a bbpack and some of them look
+ // nice, but I'm not sure if it's worth the risk of merge conflicts.
+ UNREACHABLE();
+
+#endif
+}
+
+#include "corexcep.h"
+
+//--------------------------------------------------------------------------------
+// Helper for EX_THROW_WITH_INNER()
+//
+// Clones an exception into the current domain. Also handles special cases for
+// OOM and other stuff. Making this a function so we don't inline all this logic
+// every place we call EX_THROW_WITH_INNER.
+//
+// If the "inner" is a transient exception such as OOM or ThreadAbort, this function
+// will just throw it rather than allow it to be wrapped in another exception.
+//--------------------------------------------------------------------------------
+Exception *ExThrowWithInnerHelper(Exception *inner)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ // Yes, NULL is a legal case. Makes it easier to author uniform helpers for
+ // both wrapped and normal exceptions.
+ if (inner == NULL)
+ {
+ return NULL;
+ }
+
+ if (inner == Exception::GetOOMException())
+ {
+ // We don't want to do allocations if we're already throwing an OOM!
+ PAL_CPP_THROW(Exception*, inner);
+ }
+
+ inner = inner->DomainBoundClone();
+
+ // It isn't useful to wrap OOMs and StackOverflows in other exceptions. Just throw them now.
+ //
+ if (inner->IsTransient())
+ {
+ PAL_CPP_THROW(Exception*, inner);
+ }
+ return inner;
+}
+
+#ifdef _DEBUG
+
+#ifdef _MSC_VER
+#pragma optimize("", off)
+#endif // _MSC_VER
+
+void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args)
+{
+ SUPPORTS_DAC;
+ return;
+}
+
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif // _MSC_VER
+
+#endif
+
+
+
+
+//-------------------------------------------------------------------------------------------
+// This routine will generate the most descriptive possible error message for an hresult.
+// It will generate at minimum the hex value. It will also try to generate the symbolic name
+// (E_POINTER) and the friendly description (from the message tables.)
+//
+// bNoGeekStuff suppresses hex HR codes. Use this sparingly as most error strings generated by the
+// CLR are aimed at developers, not end-users.
+//-------------------------------------------------------------------------------------------
+void GetHRMsg(HRESULT hr, SString &result, BOOL bNoGeekStuff/* = FALSE*/)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit!
+
+
+
+ SString strDescr;
+ BOOL fHaveDescr = FALSE;
+
+ if (FAILED(hr) && HRESULT_FACILITY(hr) == FACILITY_URT && HRESULT_CODE(hr) < MAX_URT_HRESULT_CODE)
+ {
+ fHaveDescr = strDescr.LoadResource(CCompRC::Error, MSG_FOR_URT_HR(hr));
+ }
+ else
+ {
+ DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM;
+ dwFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;
+
+ fHaveDescr = strDescr.FormatMessage(dwFlags, 0, hr, 0);
+ }
+
+ LPCSTR name = Exception::GetHRSymbolicName(hr);
+
+ // If we can't get a resource string, print the hresult regardless.
+ if (!fHaveDescr)
+ {
+ bNoGeekStuff = FALSE;
+ }
+
+ if (fHaveDescr)
+ {
+ result.Append(strDescr);
+ }
+
+ if (!bNoGeekStuff)
+ {
+ if (fHaveDescr)
+ {
+ result.Append(W(" ("));
+ }
+
+ result.AppendPrintf(W("0x%.8X"), hr);
+ if (name != NULL)
+ {
+ result.AppendPrintf(W(" (%S)"), name);
+ }
+
+ if (fHaveDescr)
+ {
+ result.Append(W(")"));
+ }
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------
+// Similar to GetHRMsg but phrased for top-level exception message.
+//-------------------------------------------------------------------------------------------
+void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result)
+{
+ CONTRACTL
+ {
+ GC_NOTRIGGER;
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ result = W(""); // Make sure this routine isn't an inadvertent data-leak exploit!
+
+ GetHRMsg(hresult, result);
+}
+
+//===========================================================================================
+// These abstractions hide the difference between legacy desktop CLR's (that don't support
+// side-by-side-inproc and rely on a fixed SEH code to identify managed exceptions) and
+// new CLR's that support side-by-side inproc.
+//
+// The new CLR's use a different set of SEH codes to avoid conflicting with the legacy CLR's.
+// In addition, to distinguish between EH's raised by different inproc instances of the CLR,
+// the module handle of the owning CLR is stored in ExceptionRecord.ExceptionInformation[4].
+//
+// (Note: all existing SEH's use either only slot [0] or no slots at all. We are leaving
+// slots [1] thru [3] open for future expansion.)
+//===========================================================================================
+
+// Is this exception code one of the special CLR-specific SEH codes that participate in the
+// instance-tagging scheme?
+BOOL IsInstanceTaggedSEHCode(DWORD dwExceptionCode)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ return dwExceptionCode == EXCEPTION_COMPLUS;
+}
+
+// This set of overloads generates the NumberParameters and ExceptionInformation[] array to
+// pass to RaiseException().
+//
+// Parameters:
+// exceptionArgs: a fixed-size array of size INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE.
+// This will get filled in by this function. (The module handle goes
+// in the last slot if this is a side-by-side-inproc enabled build.)
+//
+// exceptionArg1... up to four arguments that go in slots [0]..[3]. These depends
+// the specific requirements of your exception code.
+//
+// Returns:
+// The NumberParameters to pass to RaiseException().
+//
+// Basically, this is either INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE or the count of your
+// fixed arguments depending on whether this tagged-SEH-enabled build.
+//
+// This function is not permitted to fail.
+
+// (the existing system can support more overloads up to 4 fixed arguments but we don't need them at this time.)
+
+static DWORD MarkAsThrownByUsWorker(UINT numArgs, /*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0 = 0)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ _ASSERTE(numArgs < INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE);
+ FillMemory(exceptionArgs, sizeof(ULONG_PTR) * INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE, 0);
+
+ exceptionArgs[0] = arg0;
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] = (ULONG_PTR)GetClrModuleBase();
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+
+ return INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE;
+}
+
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE])
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return MarkAsThrownByUsWorker(0, exceptionArgs);
+}
+
+DWORD MarkAsThrownByUs(/*out*/ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE], ULONG_PTR arg0)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return MarkAsThrownByUsWorker(1, exceptionArgs, arg0);
+}
+
+// Given an exception record, checks if it's exception code matches a specific exception code
+// *and* whether it was tagged by the calling instance of the CLR.
+//
+// If this is a non-tagged-SEH-enabled build, it is blindly assumed to be tagged by the
+// calling instance of the CLR.
+BOOL WasThrownByUs(const EXCEPTION_RECORD *pcER, DWORD dwExceptionCode)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ _ASSERTE(IsInstanceTaggedSEHCode(dwExceptionCode));
+ _ASSERTE(pcER != NULL);
+ if (dwExceptionCode != pcER->ExceptionCode)
+ {
+ return FALSE;
+ }
+
+ if (pcER->NumberParameters != INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE)
+ {
+ return FALSE;
+ }
+#if!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ if ((ULONG_PTR)GetClrModuleBase() != pcER->ExceptionInformation[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE - 1] )
+ {
+ return FALSE;
+ }
+ return TRUE;
+#else // !(!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ return FALSE;
+#endif // !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+}
+
+
+
+//-----------------------------------------------------------------------------------
+// The following group wraps the basic abstracts specifically for EXCEPTION_COMPLUS.
+//-----------------------------------------------------------------------------------
+BOOL IsComPlusException(const EXCEPTION_RECORD *pcER)
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ return WasThrownByUs(pcER, EXCEPTION_COMPLUS);
+}
+
+VOID RaiseComPlusException()
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ ULONG_PTR exceptionArgs[INSTANCE_TAGGED_SEH_PARAM_ARRAY_SIZE];
+ DWORD numParams = MarkAsThrownByUs(exceptionArgs);
+ RaiseException(EXCEPTION_COMPLUS, 0, numParams, exceptionArgs);
+}
+
+//===========================================================================================
+//===========================================================================================
diff --git a/src/coreclr/utilcode/format1.cpp b/src/coreclr/utilcode/format1.cpp
new file mode 100644
index 00000000000..f8ef94406bf
--- /dev/null
+++ b/src/coreclr/utilcode/format1.cpp
@@ -0,0 +1,119 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/***************************************************************************/
+/* routines for parsing file format stuff ... */
+/* this is split off from format.cpp because this uses meta-data APIs that
+ are not present in many builds. Thus if someone needs things in the format.cpp
+ file but does not have the meta-data APIs, I want it to link */
+
+#include "stdafx.h"
+#include "cor.h"
+#include "corpriv.h"
+
+//---------------------------------------------------------------------------------------
+//
+static LONG FilterAllExceptions(PEXCEPTION_POINTERS pExceptionPointers, LPVOID lpvParam)
+{
+ if ((pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) ||
+ (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_ARRAY_BOUNDS_EXCEEDED) ||
+ (pExceptionPointers->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR))
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+//---------------------------------------------------------------------------------------
+//
+COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER(
+ COR_ILMETHOD * header,
+ void * pInternalImport,
+ DecoderStatus * wbStatus)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ // Can't put contract because of SEH
+ // CONTRACTL
+ // {
+ // NOTHROW;
+ // GC_NOTRIGGER;
+ // FORBID_FAULT;
+ // }
+ // CONTRACTL_END
+
+ bool fErrorInInit = false;
+ struct Param
+ {
+ COR_ILMETHOD_DECODER * pThis;
+ COR_ILMETHOD * header;
+ } param;
+ param.pThis = this;
+ param.header = header;
+
+ PAL_TRY(Param *, pParam, &param)
+ {
+ // Decode the COR header into a more convenient form
+ DecoderInit(pParam->pThis, pParam->header);
+ }
+ PAL_EXCEPT_FILTER(FilterAllExceptions)
+ {
+ fErrorInInit = true;
+ Code = 0;
+ SetLocalVarSigTok(0);
+ if (wbStatus != NULL)
+ {
+ *wbStatus = FORMAT_ERROR;
+ }
+ }
+ PAL_ENDTRY
+
+ if (fErrorInInit)
+ {
+ return;
+ }
+
+ // If there is a local variable sig, fetch it into 'LocalVarSig'
+ if ((GetLocalVarSigTok() != 0) && (pInternalImport != NULL))
+ {
+ IMDInternalImport * pMDI = reinterpret_cast<IMDInternalImport *>(pInternalImport);
+
+ if (wbStatus != NULL)
+ {
+ if ((!pMDI->IsValidToken(GetLocalVarSigTok())) ||
+ (TypeFromToken(GetLocalVarSigTok()) != mdtSignature) ||
+ (RidFromToken(GetLocalVarSigTok()) == 0))
+ {
+ *wbStatus = FORMAT_ERROR; // failure bad local variable signature token
+ return;
+ }
+ }
+
+ if (FAILED(pMDI->GetSigFromToken(GetLocalVarSigTok(), &cbLocalVarSig, &LocalVarSig)))
+ {
+ // Failure bad local variable signature token
+ if (wbStatus != NULL)
+ {
+ *wbStatus = FORMAT_ERROR;
+ }
+ LocalVarSig = NULL;
+ cbLocalVarSig = 0;
+ return;
+ }
+
+ if (wbStatus != NULL)
+ {
+ if (FAILED(validateTokenSig(GetLocalVarSigTok(), LocalVarSig, cbLocalVarSig, 0, pMDI)) ||
+ (*LocalVarSig != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG))
+ {
+ *wbStatus = VERIFICATION_ERROR; // failure validating local variable signature
+ return;
+ }
+ }
+ }
+
+ if (wbStatus != NULL)
+ {
+ *wbStatus = SUCCESS;
+ }
+} // COR_ILMETHOD_DECODER::COR_ILMETHOD_DECODER
diff --git a/src/coreclr/utilcode/fstream.cpp b/src/coreclr/utilcode/fstream.cpp
new file mode 100644
index 00000000000..835e24f6249
--- /dev/null
+++ b/src/coreclr/utilcode/fstream.cpp
@@ -0,0 +1,294 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h" // Precompiled header key.
+#include "fstream.h"
+
+CFileStream::CFileStream()
+: _cRef(1)
+, _hFile(INVALID_HANDLE_VALUE)
+{
+}
+
+CFileStream::~CFileStream()
+{
+ Close();
+}
+
+HRESULT CFileStream::OpenForRead(LPCWSTR wzFilePath)
+{
+ HRESULT hr = S_OK;
+ DWORD dwShareMode = FILE_SHARE_READ;
+
+ dwShareMode |= FILE_SHARE_DELETE;
+
+ _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath);
+ if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ _hFile = WszCreateFile(wzFilePath, GENERIC_READ,
+ dwShareMode, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Exit;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::OpenForWrite(LPCWSTR wzFilePath)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(_hFile == INVALID_HANDLE_VALUE && wzFilePath);
+ if (_hFile != INVALID_HANDLE_VALUE || !wzFilePath) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ _hFile = WszCreateFile(wzFilePath, GENERIC_WRITE,
+ FILE_SHARE_READ, NULL,
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (_hFile == INVALID_HANDLE_VALUE)
+ {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ goto Exit;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::QueryInterface(REFIID riid, void **ppv)
+{
+ HRESULT hr = S_OK;
+
+ if (!ppv)
+ return E_POINTER;
+
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IStream)) {
+ *ppv = static_cast<IStream *>(this);
+ }
+ else {
+ hr = E_NOINTERFACE;
+ }
+
+ if (*ppv) {
+ AddRef();
+ }
+
+ return hr;
+}
+
+STDMETHODIMP_(ULONG) CFileStream::AddRef()
+{
+ return InterlockedIncrement(&_cRef);
+}
+
+STDMETHODIMP_(ULONG) CFileStream::Release()
+{
+ ULONG ulRef = InterlockedDecrement(&_cRef);
+
+ if (!ulRef) {
+ delete this;
+ }
+
+ return ulRef;
+}
+
+HRESULT CFileStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
+{
+ HRESULT hr = S_OK;
+ ULONG cbRead = 0;
+
+ if (pcbRead != NULL) {
+ *pcbRead = 0;
+ }
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ if (!::ReadFile(_hFile, pv, cb, &cbRead, NULL)) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+ if (cbRead == 0) {
+ hr = S_FALSE;
+ }
+ else {
+ hr = NOERROR;
+ }
+
+ if (pcbRead != NULL) {
+ *pcbRead = cbRead;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Write(void const *pv, ULONG cb, ULONG *pcbWritten)
+{
+ HRESULT hr = S_OK;
+ ULONG cbWritten = 0;
+
+ if (pcbWritten != NULL) {
+ *pcbWritten = 0;
+ }
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ if (!::WriteFile(_hFile, pv, cb, &cbWritten, NULL)) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+ if (cbWritten == 0) {
+ hr = S_FALSE;
+ }
+ else {
+ hr = S_OK;
+ }
+
+ if (pcbWritten != NULL) {
+ *pcbWritten = cbWritten;
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
+{
+#if 1 // SetFilePointerEx not supported on Win9x
+ return E_NOTIMPL;
+#else
+ HRESULT hr = S_OK;
+ DWORD dwFileOrigin;
+ BOOL bRet;
+
+ _ASSERTE(_hFile != INVALID_HANDLE_VALUE);
+ if (_hFile == INVALID_HANDLE_VALUE) {
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ switch (dwOrigin) {
+ case STREAM_SEEK_SET:
+ dwFileOrigin = FILE_BEGIN;
+ break;
+
+ case STREAM_SEEK_CUR:
+ dwFileOrigin = FILE_CURRENT;
+ break;
+
+ case STREAM_SEEK_END:
+ dwFileOrigin = FILE_END;
+ break;
+
+ default:
+ hr = E_UNEXPECTED;
+ goto Exit;
+ }
+
+ bRet = SetFilePointerEx(_hFile, dlibMove, (LARGE_INTEGER *)plibNewPosition,
+ dwFileOrigin);
+ if (!bRet) {
+ hr = HRESULT_FROM_WIN32(::GetLastError());
+ goto Exit;
+ }
+
+
+Exit:
+ return hr;
+#endif
+}
+
+HRESULT CFileStream::SetSize(ULARGE_INTEGER libNewSize)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Commit(DWORD grfCommitFlags)
+{
+ HRESULT hr = S_OK;
+
+ if (grfCommitFlags != 0) {
+ hr = E_INVALIDARG;
+ goto Exit;
+ }
+
+ if (!Close()) {
+ hr = HRESULT_FROM_WIN32(GetLastError());
+ }
+
+Exit:
+ return hr;
+}
+
+HRESULT CFileStream::Revert()
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
+{
+ return E_NOTIMPL;
+}
+
+HRESULT CFileStream::Clone(IStream **ppIStream)
+{
+ return E_NOTIMPL;
+}
+
+
+BOOL CFileStream::Close()
+{
+ BOOL fSuccess = FALSE;
+
+ if (_hFile != INVALID_HANDLE_VALUE) {
+ if (!::CloseHandle(_hFile)) {
+ _hFile = INVALID_HANDLE_VALUE;
+ goto Exit;
+ }
+
+ _hFile = INVALID_HANDLE_VALUE;
+ }
+
+ fSuccess = TRUE;
+
+Exit:
+ return fSuccess;
+}
+
diff --git a/src/coreclr/utilcode/fstring.cpp b/src/coreclr/utilcode/fstring.cpp
new file mode 100644
index 00000000000..0f4360e9ffe
--- /dev/null
+++ b/src/coreclr/utilcode/fstring.cpp
@@ -0,0 +1,321 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// FString.cpp
+//
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "ex.h"
+#include "holder.h"
+
+#include "fstring.h"
+
+
+namespace FString
+{
+
+#ifdef _MSC_VER
+#pragma optimize("t", on)
+#endif // _MSC_VER
+
+#define MAX_LENGTH 0x1fffff00
+
+
+HRESULT Unicode_Utf8_Length(__in_z LPCWSTR pString, __out bool * pAllAscii, __out DWORD * pLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ * pAllAscii = true;
+
+ LPCWSTR p = pString;
+
+ while (true)
+ {
+ WCHAR ch = * p;
+
+ // Single check for termination and non ASCII
+ if (((unsigned) (ch - 1)) >= 0x7F)
+ {
+ if (ch != 0)
+ {
+ * pAllAscii = false;
+ }
+
+ break;
+ }
+
+ p ++;
+ }
+
+ if (* pAllAscii)
+ {
+ if ((p - pString) > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+
+ * pLength = (DWORD) (p - pString);
+ }
+ else // use WideCharToMultiByte to calculate result length
+ {
+ * pLength = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, NULL, 0, NULL, NULL);
+
+ if (*pLength == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Remove the count of null terminator, to be consistent with the all-ASCII case.
+ --*pLength;
+
+ if (*pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UNICODE to UTF8
+HRESULT Unicode_Utf8(__in_z LPCWSTR pString, bool allAscii, __out_z LPSTR pBuffer, DWORD length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ pBuffer[length] = 0;
+
+ if (allAscii)
+ {
+ LPCWSTR p = pString;
+
+ LPSTR q = pBuffer;
+
+ LPCWSTR endP = p + length - 8;
+
+ // Unfold to optimize for long string: 8 chars per iteration
+ while (p < endP)
+ {
+ q[0] = (char) p[0];
+ q[1] = (char) p[1];
+ q[2] = (char) p[2];
+ q[3] = (char) p[3];
+
+ q[4] = (char) p[4];
+ q[5] = (char) p[5];
+ q[6] = (char) p[6];
+ q[7] = (char) p[7];
+
+ q += 8;
+ p += 8;
+ }
+
+ endP += 8;
+
+ while (p < endP)
+ {
+ * q ++ = (char) * p ++;
+ }
+ }
+ else
+ {
+ length = WszWideCharToMultiByte(CP_UTF8, 0, pString, -1, pBuffer, (int) length + 1, NULL, NULL);
+
+ if (length == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT Utf8_Unicode_Length(__in_z LPCSTR pString, __out bool * pAllAscii, __out DWORD * pLength)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ * pAllAscii = true;
+
+ LPCSTR p = pString;
+
+ while (true)
+ {
+ char ch = * p;
+
+ // Single check for termination and non ASCII
+ if (((unsigned) (ch - 1)) >= 0x7F)
+ {
+ if (ch != 0)
+ {
+ * pAllAscii = false;
+ }
+
+ break;
+ }
+
+ p ++;
+ }
+
+ if (* pAllAscii)
+ {
+ if ((p - pString) > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+
+ * pLength = (DWORD)(p - pString);
+ }
+ else
+ {
+ * pLength = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, NULL, 0);
+
+ if (* pLength == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Remove the count of null terminator, to be consistent with the all-ASCII case.
+ --*pLength;
+
+ if (* pLength > MAX_LENGTH)
+ {
+ return COR_E_OVERFLOW;
+ }
+ }
+
+ return S_OK;
+}
+
+
+// UTF8 to Unicode
+
+HRESULT Utf8_Unicode(__in_z LPCSTR pString, bool allAscii, __out_z LPWSTR pBuffer, DWORD length)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ pBuffer[length] = 0;
+
+ if (allAscii)
+ {
+ LPCSTR p = pString;
+
+ LPWSTR q = pBuffer;
+
+ LPCSTR endP = p + length - 8;
+
+ // Unfold to optimize for long string: 4 chars per iteration
+ while (p < endP)
+ {
+ q[0] = (WCHAR) p[0];
+ q[1] = (WCHAR) p[1];
+ q[2] = (WCHAR) p[2];
+ q[3] = (WCHAR) p[3];
+
+ q[4] = (WCHAR) p[4];
+ q[5] = (WCHAR) p[5];
+ q[6] = (WCHAR) p[6];
+ q[7] = (WCHAR) p[7];
+
+ q += 8;
+ p += 8;
+ }
+
+ endP += 8;
+
+ while (p < endP)
+ {
+ * q ++ = (WCHAR) * p ++;
+ }
+ }
+ else
+ {
+ length = WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, pBuffer, (int) length + 1);
+
+ if (length == 0)
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+ }
+
+ return S_OK;
+}
+
+
+HRESULT ConvertUnicode_Utf8(__in_z LPCWSTR pString, __out_z LPSTR * pBuffer)
+{
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = Unicode_Utf8_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ * pBuffer = new (nothrow) char[length + 1];
+
+ if (* pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = Unicode_Utf8(pString, allAscii, * pBuffer, length);
+ }
+ }
+
+ return hr;
+}
+
+
+HRESULT ConvertUtf8_Unicode(__in_z LPCSTR pString, __out_z LPWSTR * pBuffer)
+{
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = Utf8_Unicode_Length(pString, & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ * pBuffer = new (nothrow) WCHAR[length + 1];
+
+ if (* pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ hr = Utf8_Unicode(pString, allAscii, * pBuffer, length);
+ }
+ }
+
+ return hr;
+}
+
+
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif // _MSC_VER
+
+} // namespace FString
diff --git a/src/coreclr/utilcode/guidfromname.cpp b/src/coreclr/utilcode/guidfromname.cpp
new file mode 100644
index 00000000000..6c8e9917704
--- /dev/null
+++ b/src/coreclr/utilcode/guidfromname.cpp
@@ -0,0 +1,235 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// GuidFromName
+
+/**
+
+Algorithm from Internet Draft document "UUIDs and GUIDs"
+By Paul J. Leach and Rich Sals, February 4, 1998.
+
+This function has been adapted from the routines in the document
+ uuid_create_from_name and format_uuid_v3
+
+Changes from documented routines:
+1. Changed all instances of uuid_t to GUID.
+ uuid_t field time_low is GUID field Data1.
+ uuid_t field time_mid is GUID field Data2.
+ uuid_t field time_hi_and_version is GUID field Data3.
+ uuid_t field clock_seq_hi_and_reserved is GUID field Data4[0].
+ uuid_t field clock_seq_low is GUID field Data4[1].
+ uuid_t field node[6] is GUID field Data4[2] through Data4[8].
+
+2. Use a c++ implementation of the md5 cryptographic hash function.
+
+3. Implemented the htonl, htons, ntohl, ntohs socket routines as inlines.
+
+4. Renamed variables and types to suit my biases.
+
+
+Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
+Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
+Digital Equipment Corporation, Maynard, Mass.
+To anyone who acknowledges that this file is provided "AS IS"
+without any express or implied warranty: permission to use, copy,
+modify, and distribute this file for any purpose is hereby
+granted without fee, provided that the above copyright notices and
+this notice appears in all source code copies, and that none of
+the names of Open Software Foundation, Inc., Hewlett-Packard
+Company, or Digital Equipment Corporation be used in advertising
+or publicity pertaining to distribution of the software without
+specific, written prior permission. Neither Open Software
+Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
+Corporation makes any representations about the suitability of
+this software for any purpose.
+
+
+Copyright(C) The Internet Society 1997. All Rights Reserved.
+
+This document and translations of it may be copied and furnished to others,
+and derivative works that comment on or otherwise explain it or assist in
+its implementation may be prepared, copied, published and distributed, in
+whole or in part, without restriction of any kind, provided that the above
+copyright notice and this paragraph are included on all such copies and
+derivative works.However, this document itself may not be modified in any
+way, such as by removing the copyright notice or references to the Internet
+Society or other Internet organizations, except as needed for the purpose of
+developing Internet standards in which case the procedures for copyrights
+defined in the Internet Standards process must be followed, or as required
+to translate it into languages other than English.
+
+The limited permissions granted above are perpetual and will not be revoked
+by the Internet Society or its successors or assigns.
+
+This document and the information contained herein is provided on an "AS IS"
+basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE
+DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY
+RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
+PARTICULAR PURPOSE.
+
+*/
+
+#include "stdafx.h"
+
+#include "md5.h" // cryptographic hash function
+#include "guidfromname.h" // verify our function signature
+#include "contract.h"
+
+#if BIGENDIAN
+#define BigEndian() true
+#else
+#define BigEndian() false
+#endif
+
+//=============================================================================
+// htons, htonl, ntohs, ntohl equivalents copied and adapted from socket library.
+//=============================================================================
+
+// HostToNetworkLong converts a 32-bit long to network byte order
+
+inline ULONG HostToNetworkLong(ULONG hostlong)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return hostlong;
+ else
+ return ( (hostlong >> 24) & 0x000000FFL) |
+ ( (hostlong >> 8) & 0x0000FF00L) |
+ ( (hostlong << 8) & 0x00FF0000L) |
+ ( (hostlong << 24) & 0xFF000000L);
+}
+
+// HostToNetworkLong converts a 16-bit short to network byte order
+
+inline USHORT HostToNetworkShort(USHORT hostshort)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return hostshort;
+ else
+ return ((hostshort >> 8) & 0x00FF) | ((hostshort << 8) & 0xFF00);
+}
+
+// NetworkToHostLong converts a 32-bit long to local host byte order
+
+inline ULONG NetworkToHostLong(ULONG netlong)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return netlong;
+ else
+ return ( (netlong >> 24) & 0x000000FFL) |
+ ( (netlong >> 8) & 0x0000FF00L) |
+ ( (netlong << 8) & 0x00FF0000L) |
+ ( (netlong << 24) & 0xFF000000L);
+}
+
+// NetworkToHostShort converts a 16-bit short to local host byte order
+
+inline USHORT NetworkToHostShort(USHORT netshort)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (BigEndian())
+ return netshort;
+ else
+ return ((netshort >> 8) & 0x00FF) | ((netshort << 8) & 0xFF00);
+}
+
+//=============================================================================
+// GuidFromName(GUID * pGuidResult, REFGUID refGuidNsid,
+// const void * pvName, DWORD dwcbName);
+//=============================================================================
+
+void GuidFromName
+(
+ GUID * pGuidResult, // resulting GUID
+ REFGUID refGuidNsid, // Name Space GUID, so identical names from
+ // different name spaces generate different GUIDs
+ const void * pvName, // the name from which to generate a GUID
+ DWORD dwcbName // name length in bytes
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ MD5 md5; // Cryptographic hash class instance
+ MD5HASHDATA md5HashData; // 128-bit hash result
+ GUID guidNsid; // context NameSpace GUID in network byte order
+
+ GUID guidTemp;
+
+ // put name space ID in network byte order so it hashes the same
+ // no matter what endian machine we're on
+ guidNsid = refGuidNsid;
+
+ // The sample code in the IETF draft document discards the result of
+ // htonl and htons. I've implemented what I think is meant and I've
+ // sent a note to the author asking for confirmation that this is
+ // his intent.
+ if (!BigEndian()) // evaluated at compile time in retail builds
+ {
+ guidNsid.Data1 = HostToNetworkLong (guidNsid.Data1);
+ guidNsid.Data2 = HostToNetworkShort(guidNsid.Data2);
+ guidNsid.Data3 = HostToNetworkShort(guidNsid.Data3);
+ }
+
+ md5.Init();
+ md5.HashMore(&guidNsid, sizeof(GUID));
+ md5.HashMore(pvName, dwcbName);
+ md5.GetHashValue(&md5HashData);
+
+ // the hash is in network byte order at this point
+ memcpy(&guidTemp, &md5HashData, sizeof(GUID));
+
+ // Remainder adapted from function "format_uuid_v3" in IETF draft document
+ // Construct a version 3 uuid with the pseudo-random number plus a few constants.
+ // convert GUID from network order to local byte order
+ if (!BigEndian()) // evaluated at compile time in retail builds
+ {
+ guidTemp.Data1 = NetworkToHostLong (guidTemp.Data1);
+ guidTemp.Data2 = NetworkToHostShort(guidTemp.Data2);
+ guidTemp.Data3 = NetworkToHostShort(guidTemp.Data3);
+ }
+
+ // set version number
+ guidTemp.Data3 &= 0x0FFF; // clear version number nibble
+ guidTemp.Data3 |= (3 << 12);// set version 3 = name-based
+
+ // set variant field
+ guidTemp.Data4[0] &= 0x3F; // clear variant bits
+ guidTemp.Data4[0] |= 0x80; // set variant = 100b
+
+ // If two GuidFromName calls were made from different threads with the same parameters,
+ // we may get incorrect result even though the expected result is the same, because
+ // GuidFromName is operating on the same pGuidResult buffer.
+ // Fix this problem by using a temp GUID buffer and then copy to the pGuidResult buffer.
+ memcpy(pGuidResult, &guidTemp, sizeof(GUID));
+}
+
+
+// This guid is used for calling GuidFromName function as COM+ runtime uniqualifier
+//
+// {69F9CBC9-DA05-11d1-9408-0000F8083460}
+static const GUID COMPLUS_RUNTIME_GUID = {0x69f9cbc9, 0xda05, 0x11d1,
+ {0x94, 0x8, 0x0, 0x0, 0xf8, 0x8, 0x34, 0x60}};
+
+void CorGuidFromNameW
+(
+ GUID * pGuidResult, // resulting GUID
+ LPCWSTR wzName, // the unicode name from which to generate a GUID
+ SIZE_T cchName // name length in count of unicode character
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ GuidFromName(
+ pGuidResult,
+ COMPLUS_RUNTIME_GUID,
+ wzName,
+ (DWORD)((cchName == (SIZE_T) -1 ? (wcslen(wzName)+1) : cchName) * sizeof(WCHAR)));
+}
diff --git a/src/coreclr/utilcode/hostimpl.cpp b/src/coreclr/utilcode/hostimpl.cpp
new file mode 100644
index 00000000000..c1e4b53b065
--- /dev/null
+++ b/src/coreclr/utilcode/hostimpl.cpp
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+#include "mscoree.h"
+#include "clrinternal.h"
+#include "clrhost.h"
+#include "ex.h"
+
+thread_local size_t t_ThreadType;
+
+CRITSEC_COOKIE ClrCreateCriticalSection(CrstType crstType, CrstFlags flags)
+{
+ CRITICAL_SECTION *cs = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(cs);
+ return (CRITSEC_COOKIE)cs;
+}
+
+void ClrDeleteCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ DeleteCriticalSection((CRITICAL_SECTION*)cookie);
+ free(cookie);
+}
+
+void ClrEnterCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ EnterCriticalSection((CRITICAL_SECTION*)cookie);
+}
+
+void ClrLeaveCriticalSection(CRITSEC_COOKIE cookie)
+{
+ _ASSERTE(cookie);
+ LeaveCriticalSection((CRITICAL_SECTION*)cookie);
+}
+
+DWORD ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable)
+{
+ return SleepEx(dwMilliseconds, bAlertable);
+}
+
+LPVOID ClrVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect)
+{
+#ifdef FAILPOINTS_ENABLED
+ if (RFS_HashStack ())
+ return NULL;
+#endif
+ return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+}
+
+BOOL ClrVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType)
+{
+ return VirtualFree(lpAddress, dwSize, dwFreeType);
+}
+
+SIZE_T ClrVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength)
+{
+ return VirtualQuery(lpAddress, lpBuffer, dwLength);
+}
+
+BOOL ClrVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
+{
+ return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
+}
+
+//------------------------------------------------------------------------------
+// Helper function to get an exception from outside the exception. In
+// the CLR, it may be from the Thread object. Non-CLR users have no thread object,
+// and it will do nothing.
+
+void GetLastThrownObjectExceptionFromThread(Exception** ppException)
+{
+ *ppException = NULL;
+}
+
+#ifdef HOST_WINDOWS
+void CreateCrashDumpIfEnabled(bool stackoverflow)
+{
+}
+#endif
diff --git a/src/coreclr/utilcode/iallocator.cpp b/src/coreclr/utilcode/iallocator.cpp
new file mode 100644
index 00000000000..9e1ea5ed906
--- /dev/null
+++ b/src/coreclr/utilcode/iallocator.cpp
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h" // Precompiled header key.
+#include "iallocator.h"
+#include "defaultallocator.h"
+
+// static
+DefaultAllocator DefaultAllocator::s_singleton;
diff --git a/src/coreclr/utilcode/ilformatter.cpp b/src/coreclr/utilcode/ilformatter.cpp
new file mode 100644
index 00000000000..9c8011dc666
--- /dev/null
+++ b/src/coreclr/utilcode/ilformatter.cpp
@@ -0,0 +1,818 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/***************************************************************************/
+/* ILFormatter.h */
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <cor.h>
+#include <debugmacros.h> // for ASSERTE
+#include "ilformatter.h"
+#include "outstring.h"
+#include "opinfo.h"
+
+/***************************************************************************/
+void ILFormatter::init(IMetaDataImport* aMeta, const BYTE* aStart,
+ const BYTE* aLimit, unsigned maxStack, const COR_ILMETHOD_SECT_EH* eh) {
+ this->~ILFormatter(); // clean out old stuff
+
+ meta = aMeta;
+ start = aStart;
+ limit = aLimit;
+ if (maxStack == 0) maxStack++;
+ stackStart = stackCur = new StackEntry[maxStack];
+ stackEnd = stackStart + maxStack;
+ targetStart = targetCur = targetEnd = 0;
+ if (eh != 0) {
+ COR_ILMETHOD_SECT_EH_CLAUSE_FAT buff;
+ const COR_ILMETHOD_SECT_EH_CLAUSE_FAT* clause;
+ for(unsigned i = 0; i < eh->EHCount(); i++) {
+ clause = (COR_ILMETHOD_SECT_EH_CLAUSE_FAT*)eh->EHClause(i, &buff);
+ // is it a regular catch clause ?
+ if ((clause->GetFlags() & (COR_ILEXCEPTION_CLAUSE_FINALLY | COR_ILEXCEPTION_CLAUSE_FAULT)) == 0)
+ setTarget(clause->GetHandlerOffset(), 1);
+ if(clause->GetFlags() & COR_ILEXCEPTION_CLAUSE_FILTER)
+ setTarget(clause->GetFilterOffset(), 1);
+ }
+ }
+}
+
+/***************************************************************************/
+inline size_t ILFormatter::stackDepth() {
+ return(stackCur - stackStart);
+}
+
+/***************************************************************************/
+inline void ILFormatter::pushAndClear(OutString* val, int prec) {
+ if (stackCur >= stackEnd) {
+ _ASSERTE(!"Stack Overflow (can be ignored)");
+ return; // Ignore overflow in free build
+ }
+ stackCur->val.swap(*val);
+ val->clear();
+ stackCur->prec = prec;
+ stackCur++;
+}
+
+/***************************************************************************/
+inline OutString* ILFormatter::top() {
+ if (stackDepth() == 0) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackStart->val.clear();
+ stackStart->val << "<UNDERFLOW ERROR>";
+ return (&stackStart->val);
+ }
+ return(&stackCur[-1].val);
+}
+
+/***************************************************************************/
+inline OutString* ILFormatter::pop(int prec) {
+ if (stackDepth() == 0) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackStart->val.clear();
+ stackStart->val << "<UNDERFLOW ERROR>";
+ return (&stackStart->val);
+ }
+ --stackCur;
+ if (stackCur->prec < prec) {
+ stackCur->val.prepend('(');
+ stackCur->val << ')';
+ }
+ return(&stackCur->val);
+}
+
+/***************************************************************************/
+inline void ILFormatter::popN(size_t num) {
+ if (stackCur-stackStart < (SSIZE_T)num) {
+ _ASSERTE(!"Stack underflow (can be ignored)");
+ stackCur = stackStart;
+ return;
+ }
+ stackCur -= num;
+}
+
+/***************************************************************************/
+void ILFormatter::setStackAsTarget(size_t ilOffset) {
+
+ Target*ptr = targetStart;
+ for(;;) {
+ if (ptr >= targetCur)
+ return;
+ if (ptr->ilOffset == ilOffset)
+ break;
+ ptr++;
+ }
+
+ for(size_t i = 0; i < ptr->stackDepth; i++) {
+ stackStart[i].val.clear();
+ stackStart[i].val << "@STK" << (unsigned)i;
+ }
+ stackCur = stackStart + ptr->stackDepth;
+}
+
+/***************************************************************************/
+void ILFormatter::setTarget(size_t ilOffset, size_t depth) {
+ if (depth == 0)
+ return;
+
+ if (targetCur >= targetEnd) {
+ Target* targetOld = targetStart;
+ size_t oldLen = targetCur-targetStart;
+ targetStart = new Target[oldLen+10];
+ targetEnd = &targetStart[oldLen+10];
+ targetCur = &targetStart[oldLen];
+ memcpy(targetStart, targetOld, sizeof(Target)*oldLen);
+ delete [] targetOld;
+ }
+ targetCur->ilOffset = ilOffset;
+ targetCur->stackDepth = depth;
+ targetCur++;
+}
+
+/***************************************************************************/
+void ILFormatter::spillStack(OutString* out) {
+
+ for(unsigned i = 0; i < stackDepth(); i++) {
+ // don't bother spilling something already spilled.
+ if (memcmp(stackStart[i].val.val(), "@STK", 4) != 0)
+ *out << "@STK" << i << " = " << stackStart[i].val.val() << "\n";
+ stackStart[i].val.clear();
+ stackStart[i].val << "@STK" << i ;
+ }
+}
+
+/***************************************************************************/
+const BYTE* ILFormatter::formatInstr(const BYTE* instrPtr, OutString* out) {
+
+ _ASSERTE(start < instrPtr && instrPtr < limit);
+ OpArgsVal arg;
+ OpInfo op;
+ instrPtr = op.fetch(instrPtr, &arg);
+ *out << op.getName();
+ if (op.getArgsInfo() != InlineNone)
+ *out << ' ';
+ formatInstrArgs(op, arg, out, instrPtr - start);
+ return(instrPtr);
+}
+
+/***************************************************************************/
+void ILFormatter::formatArgs(unsigned numArgs, OutString* out) {
+
+ *out << '(';
+ if (numArgs > stackDepth()) {
+ _ASSERTE(!"Underflow error");
+ *out << "<UNDERFLOW ERROR>";
+ }
+ else {
+ popN(numArgs);
+ for(unsigned i = 0; i < numArgs; i++) {
+ if (i != 0) *out << ", ";
+ *out << stackCur[i].val.val();
+ }
+ }
+ *out << ')';
+}
+
+/***************************************************************************/
+void ILFormatter::formatInstrArgs(OpInfo op, OpArgsVal arg, OutString* out, size_t curILOffset) {
+
+ MDUTF8CSTR typeName=0;
+ HRESULT hr = S_OK;
+ switch(op.getArgsInfo() & PrimaryMask) {
+ case InlineNone:
+ break;
+ case InlineVar:
+ *out << arg.i;
+ break;
+ case InlineI:
+ case InlineRVA:
+ out->hex(arg.i, 0, OutString::put0x);
+ break;
+ case InlineR:
+ *out << arg.r;
+ break;
+ case InlineBrTarget: {
+ _ASSERTE(curILOffset != INVALID_IL_OFFSET);
+ size_t target = curILOffset + arg.i;
+ setTarget(target, stackDepth());
+ *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ } break;
+ case InlineI8:
+ out->hex(arg.i, 0, OutString::put0x);
+ break;
+ case InlineString: {
+ ULONG numChars;
+ WCHAR str[84];
+
+ hr = meta->GetUserString(arg.i, str, 80, &numChars);
+ _ASSERTE(SUCCEEDED(hr));
+ if (numChars < 80)
+ str[numChars] = 0;
+ wcscpy_s(&str[79], 4, W("..."));
+ *out << '"';
+ WCHAR* ptr = str;
+ while(*ptr != 0) {
+ if (*ptr == '\n')
+ *out << "\\n";
+ else if (*ptr == '"')
+ *out << "\\\"";
+ else if (*ptr < 0x20 || * ptr >= 0x80) {
+ *out << '\\';
+ out->hex(*ptr, 4, OutString::zeroFill);
+ }
+ else
+ *out << char(*ptr);
+ ptr++;
+ }
+ *out << '"';
+ } break;
+ case InlineMethod:
+ case InlineField:
+ case InlineTok: {
+ // Get the typeName if possible
+ mdToken mdType = mdTypeDefNil;
+ if (TypeFromToken(arg.i) == mdtMethodDef)
+ hr = meta->GetMethodProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0);
+ else if (TypeFromToken(arg.i) == mdtMemberRef)
+ hr = meta->GetMemberRefProps(mdMemberRef(arg.i), &mdType, 0, 0, 0, 0, 0);
+ else if (TypeFromToken(arg.i) == mdtFieldDef)
+ hr = meta->GetFieldProps(mdMethodDef(arg.i), &mdType, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+ if (SUCCEEDED(hr) && mdType != mdTypeDefNil) {
+ hr = meta->GetNameFromToken(mdType, &typeName);
+ }
+ }
+ FALLTHROUGH;
+ case InlineType: {
+ // FIX handle case if (TypeFromToken(arg.i) == mdtTypeSpec)
+ MDUTF8CSTR name;
+ hr = meta->GetNameFromToken(arg.i, &name);
+ if (SUCCEEDED(hr)) {
+ if (typeName) {
+ const char* lastDot = strrchr(typeName, '.');
+ if (lastDot) typeName = lastDot + 1;
+ *out << typeName << "::";
+ }
+ *out << name;
+ }
+ else {
+ *out << "TOK<";
+ out->hex(arg.i, 0, OutString::put0x);
+ *out << '>';
+ }
+ } break;
+ case InlineSig:
+ *out << "SIG<";
+ out->hex(arg.i, 0, OutString::put0x);
+ *out << '>';
+ break;
+ case InlineSwitch: {
+ _ASSERTE(curILOffset != INVALID_IL_OFFSET);
+ unsigned count = arg.switch_.count;
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ size_t target = curILOffset + GET_UNALIGNED_VAL32(&arg.switch_.targets[i]);
+ setTarget(target, stackDepth()-1);
+ *out << "IL_"; out->hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ *out << ' ';
+ }
+ } break;
+ case InlinePhi: {
+ unsigned count = arg.phi.count;
+ unsigned i;
+ for (i = 0; i < count; i++) {
+ *out << GET_UNALIGNED_VAL32(&arg.phi.vars[i]);
+ *out << ' ';
+ }
+ } break;
+ default:
+ _ASSERTE(!"BadType");
+ }
+}
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+/***************************************************************************/
+const BYTE* ILFormatter::formatStatement(const BYTE* instrPtr, OutString* out) {
+
+ OutString result;
+ OpInfo op;
+ OutString *lhs, *rhs, *idx;
+ const char* name;
+ int prec = 0;
+
+ // set stack as it would be if it was begin jumped to
+ setStackAsTarget(instrPtr - start);
+
+ while(instrPtr < limit) {
+ OpArgsVal inlineArg;
+ instrPtr = op.fetch(instrPtr, &inlineArg);
+
+ switch(op.getOpcode()) {
+ case CEE_UNALIGNED:
+ case CEE_TAILCALL:
+ case CEE_VOLATILE:
+ // for now just skip these
+ break;
+
+ case CEE_LDARGA_S:
+ case CEE_LDARGA:
+ result << "&";
+ goto DO_LDARG;
+
+ case CEE_LDARG_0:
+ case CEE_LDARG_1:
+ case CEE_LDARG_2:
+ case CEE_LDARG_3:
+ inlineArg.i = op.getOpcode() - CEE_LDARG_0;
+ goto DO_LDARG;
+
+ case CEE_LDARG:
+ case CEE_LDARG_S:
+ DO_LDARG:
+ name = "arg";
+ DO_LDARG_LDLOC:
+ result << name << inlineArg.i;
+ prec = 0x1000;
+ goto DO_PUSH;
+ DO_PUSH:
+ pushAndClear(&result, prec); // also clears result!
+ break;
+
+ case CEE_LDLOCA_S:
+ case CEE_LDLOCA:
+ result << "&";
+ goto DO_LDLOC;
+
+ case CEE_LDLOC_0:
+ case CEE_LDLOC_1:
+ case CEE_LDLOC_2:
+ case CEE_LDLOC_3:
+ inlineArg.i = op.getOpcode() - CEE_LDLOC_0;
+ goto DO_LDLOC;
+
+ case CEE_LDLOC:
+ case CEE_LDLOC_S:
+ DO_LDLOC:
+ name = "loc";
+ goto DO_LDARG_LDLOC;
+
+ case CEE_STARG:
+ case CEE_STARG_S:
+ name = "arg";
+ DO_STARG_STLOC:
+ lhs = pop(0x10);
+ result << name << inlineArg.i << " = " << lhs->val();
+ DO_STMT:
+ spillStack(out);
+ *out << result.val() << '\n';
+ // if flow of control does not fall through,
+ // assume the stack is empty
+ if (op.getFlow() == FLOW_BRANCH || op.getFlow() == FLOW_RETURN ||
+ op.getFlow() == FLOW_THROW) {
+ popN(stackDepth());
+ }
+ return(instrPtr);
+
+ case CEE_STLOC_0:
+ case CEE_STLOC_1:
+ case CEE_STLOC_2:
+ case CEE_STLOC_3:
+ inlineArg.i = op.getOpcode() - CEE_STLOC_0;
+ goto DO_STLOC;
+
+ case CEE_STLOC:
+ case CEE_STLOC_S:
+ DO_STLOC:
+ name = "loc";
+ goto DO_STARG_STLOC;
+
+ case CEE_LDC_I4_M1:
+ case CEE_LDC_I4_0:
+ case CEE_LDC_I4_1:
+ case CEE_LDC_I4_2:
+ case CEE_LDC_I4_3:
+ case CEE_LDC_I4_4:
+ case CEE_LDC_I4_5:
+ case CEE_LDC_I4_6:
+ case CEE_LDC_I4_7:
+ case CEE_LDC_I4_8:
+ inlineArg.i = op.getOpcode() - CEE_LDC_I4_0;
+ FALLTHROUGH;
+ case CEE_LDC_I4:
+ case CEE_LDC_I4_S:
+ result << inlineArg.i;
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDC_I8:
+ result.hex(inlineArg.i8);
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDC_R4:
+ case CEE_LDC_R8:
+ result << inlineArg.r;
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDNULL:
+ result << "null";
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_LDSTR:
+ formatInstrArgs(op, inlineArg, &result);
+ prec = 0x1000;
+ goto DO_PUSH;
+
+ case CEE_BEQ:
+ case CEE_BEQ_S:
+ name = "=="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGE:
+ case CEE_BGE_S:
+ name = ">="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGE_UN:
+ case CEE_BGE_UN_S:
+ name = ">=un"; prec = 0x40; goto DO_BR_BINOP;
+
+ case CEE_BGT:
+ case CEE_BGT_S:
+ name = ">"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BGT_UN:
+ case CEE_BGT_UN_S:
+ name = ">un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLE:
+ case CEE_BLE_S:
+ name = "<="; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLE_UN:
+ case CEE_BLE_UN_S:
+ name = "<=un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLT:
+ case CEE_BLT_S:
+ name = "<"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BLT_UN:
+ case CEE_BLT_UN_S:
+ name = "<un"; prec = 0x40; goto DO_BR_BINOP;
+ case CEE_BNE_UN:
+ case CEE_BNE_UN_S:
+ name = "!=un"; prec = 0x40; goto DO_BR_BINOP;
+ DO_BR_BINOP:
+ rhs = pop(prec);
+ lhs = pop(prec-1);
+ result << "if (" << lhs->val() << ' ' << name << ' ' << rhs->val() << ") ";
+ goto DO_BR;
+
+ case CEE_LEAVE_S:
+ case CEE_LEAVE:
+ while (stackDepth() > 0) {
+ lhs = pop();
+ *lhs << '\n' << result; // put the result in front of anything else
+ result.swap(*lhs);
+ }
+ FALLTHROUGH;
+ case CEE_BR_S:
+ case CEE_BR:
+ DO_BR: {
+ size_t target = (instrPtr - start) + inlineArg.i;
+ setTarget(target, stackDepth());
+ result << "goto IL_"; result.hex(static_cast<unsigned __int64>(target), 4, OutString::zeroFill);
+ } goto DO_STMT;
+
+ case CEE_BRFALSE_S:
+ case CEE_BRFALSE:
+ name = "!";
+ goto DO_BR_UNOP;
+ case CEE_BRTRUE_S:
+ case CEE_BRTRUE:
+ name = "";
+ DO_BR_UNOP:
+ lhs = pop();
+ result << "if (" << name << lhs->val() << ") ";
+ goto DO_BR;
+
+ case CEE_OR:
+ name = "|"; prec = 0x20; goto DO_BINOP;
+ case CEE_XOR:
+ name = "^"; prec = 0x20; goto DO_BINOP;
+ case CEE_AND:
+ name = "&"; prec = 0x30; goto DO_BINOP;
+ case CEE_SHL:
+ name = "<<"; prec = 0x50; goto DO_BINOP;
+ case CEE_SHR:
+ name = ">>"; prec = 0x50; goto DO_BINOP;
+ case CEE_SHR_UN:
+ name = ">>un"; prec = 0x50; goto DO_BINOP;
+ case CEE_CEQ:
+ name = "=="; prec = 0x40; goto DO_BINOP;
+ case CEE_CGT:
+ name = ">"; prec = 0x40; goto DO_BINOP;
+ case CEE_CGT_UN:
+ name = ">un"; prec = 0x40; goto DO_BINOP;
+ case CEE_CLT:
+ name = "<"; prec = 0x40; goto DO_BINOP;
+ case CEE_CLT_UN:
+ name = "<un"; prec = 0x40; goto DO_BINOP;
+ case CEE_ADD:
+ name = "+"; prec = 0x60; goto DO_BINOP;
+ case CEE_ADD_OVF:
+ name = "+ovf"; prec = 0x60; goto DO_BINOP;
+ case CEE_ADD_OVF_UN:
+ name = "+ovf.un";prec = 0x60; goto DO_BINOP;
+ case CEE_SUB:
+ name = "-"; prec = 0x60; goto DO_BINOP;
+ case CEE_SUB_OVF:
+ name = "-ovf"; prec = 0x60; goto DO_BINOP;
+ case CEE_SUB_OVF_UN:
+ name = "-ovf.un";prec = 0x60; goto DO_BINOP;
+ case CEE_MUL:
+ name = "*"; prec = 0x70; goto DO_BINOP;
+ case CEE_MUL_OVF:
+ name = "*ovf"; prec = 0x70; goto DO_BINOP;
+ case CEE_MUL_OVF_UN:
+ name = "*ovf.un";prec = 0x70; goto DO_BINOP;
+ case CEE_DIV:
+ name = "/"; prec = 0x70; goto DO_BINOP;
+ case CEE_DIV_UN:
+ name = "/un"; prec = 0x70; goto DO_BINOP;
+ case CEE_REM:
+ name = "%"; prec = 0x70; goto DO_BINOP;
+ case CEE_REM_UN:
+ name = "%un"; prec = 0x70; goto DO_BINOP;
+ DO_BINOP:
+ rhs = pop(prec);
+ lhs = pop(prec-1);
+ result << lhs->val() << ' ' << name << ' ' << rhs->val();
+ goto DO_PUSH;
+
+ case CEE_NOT:
+ name = "~"; prec = 0x80; goto DO_UNOP;
+ case CEE_NEG:
+ name = "-"; prec = 0x80; goto DO_UNOP;
+ DO_UNOP:
+ lhs = pop(prec-1);
+ result << name << lhs->val();
+ goto DO_PUSH;
+
+ case CEE_RET:
+ _ASSERTE(stackDepth() <= 1);
+ result << "return";
+ if (stackDepth() > 0) {
+ lhs = pop();
+ result << ' ' << lhs->val();
+ }
+ goto DO_STMT;
+
+ case CEE_POP:
+ lhs = pop();
+ result.swap(*lhs);
+ goto DO_STMT;
+
+ case CEE_DUP:
+ spillStack(out);
+ lhs = top();
+ result << lhs->val();
+ prec = 0x1000; // spillstack makes them temps, so they have high prec
+ goto DO_PUSH;
+
+ case CEE_LDFLDA:
+ name = "&";
+ goto DO_LDFLD_LDFLDA;
+ case CEE_LDFLD:
+ name = "";
+ DO_LDFLD_LDFLDA:
+ prec = 0x110;
+ lhs = pop(prec-1);
+ result << name << lhs->val() << '.';
+ formatInstrArgs(op, inlineArg, &result);
+ goto DO_PUSH;
+
+ case CEE_LDSFLDA:
+ name = "&";
+ goto DO_LDSFLD_LDSFLDA;
+ case CEE_LDSFLD:
+ name = "";
+ DO_LDSFLD_LDSFLDA:
+ prec = 0x1000;
+ result << name;
+ formatInstrArgs(op, inlineArg, &result);
+ goto DO_PUSH;
+
+ case CEE_STFLD:
+ rhs = pop(0x10);
+ lhs = pop(0x110-1);
+ result << lhs->val() << '.';
+ formatInstrArgs(op, inlineArg, &result);
+ result << " = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_STSFLD:
+ rhs = pop(0x20);
+ formatInstrArgs(op, inlineArg, &result);
+ result << " = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_CALLI:
+ lhs = pop();
+ result << "CALLI<" << lhs->val() << '>';
+ goto DO_CALL;
+
+ case CEE_NEWOBJ:
+ result << "new ";
+ FALLTHROUGH;
+ case CEE_CALL:
+ case CEE_CALLVIRT: {
+ formatInstrArgs(op, inlineArg, &result);
+
+ DO_CALL:
+ // Get the signature stuff
+ PCCOR_SIGNATURE sig;
+ ULONG cSig;
+ HRESULT hr;
+ if (TypeFromToken(inlineArg.i) == mdtMethodDef)
+ hr = meta->GetMethodProps(mdMethodDef(inlineArg.i), 0, 0, 0, 0, 0, &sig, &cSig, 0, 0);
+ else if (TypeFromToken(inlineArg.i) == mdtMemberRef)
+ hr = meta->GetMemberRefProps(mdMemberRef(inlineArg.i), 0, 0, 0, 0, &sig, &cSig);
+ else
+ hr = meta->GetSigFromToken(mdSignature(inlineArg.i), &sig, &cSig);
+ _ASSERTE(SUCCEEDED(hr));
+ unsigned callConv = CorSigUncompressData(sig);
+ unsigned hasThis = callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS;
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ CorSigUncompressData(sig);
+ }
+ unsigned numArgs = CorSigUncompressData(sig);
+ while(*sig == ELEMENT_TYPE_CMOD_REQD || *sig == ELEMENT_TYPE_CMOD_OPT) {
+ sig++;
+ CorSigUncompressToken(sig);
+ }
+
+ formatArgs(numArgs, &result);
+ if (hasThis && op.getOpcode() != CEE_NEWOBJ) {
+ lhs = pop(0x90);
+ result.swap(*lhs);
+ result << '.' << lhs->val();
+ }
+ prec = 0x1000;
+ if (op.getOpcode() == CEE_NEWOBJ || *sig != ELEMENT_TYPE_VOID)
+ goto DO_PUSH;
+ } goto DO_STMT;
+
+ case CEE_LDELEM_I1:
+ case CEE_LDELEM_I2:
+ case CEE_LDELEM_I4:
+ case CEE_LDELEM_I8:
+ case CEE_LDELEM_REF:
+ case CEE_LDELEM_R4:
+ case CEE_LDELEM_R8:
+ case CEE_LDELEM_U1:
+ case CEE_LDELEM_U2:
+ case CEE_LDELEM_I:
+ rhs = pop(0x100);
+ lhs = pop();
+ result << lhs->val() << '[' << rhs->val() << ']';
+ prec = 0x100;
+ goto DO_PUSH;
+
+ case CEE_STELEM_I1:
+ case CEE_STELEM_I2:
+ case CEE_STELEM_I4:
+ case CEE_STELEM_I8:
+ case CEE_STELEM_REF:
+ case CEE_STELEM_R4:
+ case CEE_STELEM_R8:
+ case CEE_STELEM_I:
+ rhs = pop(0x100);
+ idx = pop();
+ lhs = pop(0x20);
+ result << lhs->val() << '[' << idx->val() << "] = " << rhs->val();
+ goto DO_STMT;
+
+ case CEE_LDIND_I1: name = "I1"; goto DO_LDIND;
+ case CEE_LDIND_I2: name = "I2"; goto DO_LDIND;
+ case CEE_LDIND_I4: name = "I4"; goto DO_LDIND;
+ case CEE_LDIND_I8: name = "I8"; goto DO_LDIND;
+ case CEE_LDIND_I: name = "I"; goto DO_LDIND;
+ case CEE_LDIND_R4: name = "R4"; goto DO_LDIND;
+ case CEE_LDIND_R8: name = "R8"; goto DO_LDIND;
+ case CEE_LDIND_U1: name = "U1"; goto DO_LDIND;
+ case CEE_LDIND_U2: name = "U2"; goto DO_LDIND;
+ case CEE_LDIND_REF: name = "REF";goto DO_LDIND;
+ DO_LDIND:
+ prec = 0x90;
+ lhs = pop(prec);
+ result << name << "(*" << lhs->val() << ')';
+ goto DO_PUSH;
+
+ case CEE_STIND_I1: name = "I1"; goto DO_STIND;
+ case CEE_STIND_I2: name = "I2"; goto DO_STIND;
+ case CEE_STIND_I4: name = "I4"; goto DO_STIND;
+ case CEE_STIND_I8: name = "I8"; goto DO_STIND;
+ case CEE_STIND_REF: name = "REF";goto DO_STIND;
+ case CEE_STIND_R4: name = "R4"; goto DO_STIND;
+ case CEE_STIND_R8: name = "R8"; goto DO_STIND;
+ DO_STIND:
+ rhs = pop();
+ lhs = pop(0x90);
+ result << '*' << lhs->val() << " = " << name << '(' << rhs->val() << ')';
+ goto DO_STMT;
+
+ case CEE_LDVIRTFTN:
+ case CEE_ARGLIST:
+ case CEE_BREAK:
+ case CEE_ENDFILTER:
+ case CEE_CPBLK:
+ case CEE_INITBLK:
+ case CEE_LDOBJ:
+ case CEE_CPOBJ:
+ case CEE_STOBJ:
+ case CEE_INITOBJ:
+ case CEE_LOCALLOC:
+ case CEE_NOP:
+ case CEE_SWITCH:
+ case CEE_CASTCLASS:
+ case CEE_ISINST:
+ case CEE_LDLEN:
+ case CEE_JMP:
+ case CEE_NEWARR:
+ case CEE_THROW:
+ case CEE_RETHROW:
+ case CEE_LDELEM_U4:
+ case CEE_LDIND_U4:
+ case CEE_LDELEMA:
+ case CEE_ENDFINALLY:
+ case CEE_STIND_I:
+ case CEE_CKFINITE:
+ case CEE_MKREFANY:
+ case CEE_REFANYTYPE:
+ case CEE_REFANYVAL:
+ case CEE_CONV_I1:
+ case CEE_CONV_I2:
+ case CEE_CONV_I4:
+ case CEE_CONV_I8:
+ case CEE_CONV_R4:
+ case CEE_CONV_R8:
+ case CEE_CONV_R_UN:
+ case CEE_CONV_OVF_I_UN:
+ case CEE_CONV_OVF_I1_UN:
+ case CEE_CONV_OVF_I2_UN:
+ case CEE_CONV_OVF_I4_UN:
+ case CEE_CONV_OVF_I8_UN:
+ case CEE_CONV_OVF_U_UN:
+ case CEE_CONV_OVF_U1_UN:
+ case CEE_CONV_OVF_U2_UN:
+ case CEE_CONV_OVF_U4_UN:
+ case CEE_CONV_OVF_U8_UN:
+ case CEE_CONV_OVF_I1:
+ case CEE_CONV_OVF_I2:
+ case CEE_CONV_OVF_I4:
+ case CEE_CONV_OVF_I8:
+ case CEE_CONV_OVF_U1:
+ case CEE_CONV_OVF_U2:
+ case CEE_CONV_OVF_U4:
+ case CEE_CONV_OVF_U8:
+ case CEE_CONV_U4:
+ case CEE_CONV_U8:
+ case CEE_CONV_U2:
+ case CEE_CONV_U1:
+ case CEE_CONV_I:
+ case CEE_CONV_OVF_I:
+ case CEE_CONV_OVF_U:
+ case CEE_CONV_U:
+ case CEE_BOX:
+ case CEE_LDELEM:
+ case CEE_STELEM:
+ case CEE_UNBOX_ANY:
+ case CEE_UNBOX:
+ case CEE_LDFTN:
+ case CEE_LDTOKEN:
+ case CEE_SIZEOF:
+ default:
+ result << op.getName();
+ if (op.getArgsInfo() != InlineNone) {
+ result << '<';
+ formatInstrArgs(op, inlineArg, &result, instrPtr-start);
+ result << '>';
+ }
+
+ _ASSERTE(op.getNumPop() >= 0);
+ if (op.getNumPop() > 0)
+ formatArgs(op.getNumPop(), &result);
+
+ prec = 0x1000;
+ _ASSERTE(op.getNumPush() == 0 || op.getNumPush() == 1);
+ if (op.getNumPush() > 0)
+ goto DO_PUSH;
+ goto DO_STMT;
+ }
+ }
+ return(instrPtr);
+}
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+
diff --git a/src/coreclr/utilcode/loaderheap.cpp b/src/coreclr/utilcode/loaderheap.cpp
new file mode 100644
index 00000000000..8cfbba75659
--- /dev/null
+++ b/src/coreclr/utilcode/loaderheap.cpp
@@ -0,0 +1,2254 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h" // Precompiled header key.
+#include "loaderheap.h"
+#include "ex.h"
+#include "pedecoder.h"
+#define DONOT_DEFINE_ETW_CALLBACK
+#include "eventtracebase.h"
+
+#define LHF_EXECUTABLE 0x1
+
+#ifndef DACCESS_COMPILE
+
+INDEBUG(DWORD UnlockedLoaderHeap::s_dwNumInstancesOfLoaderHeaps = 0;)
+
+#ifdef RANDOMIZE_ALLOC
+#include <time.h>
+static class Random
+{
+public:
+ Random() { seed = (unsigned int)time(NULL); }
+ unsigned int Next()
+ {
+ return ((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff;
+ }
+private:
+ unsigned int seed;
+} s_random;
+#endif
+
+namespace
+{
+#if !defined(SELF_NO_HOST) // ETW available only in the runtime
+ inline void EtwAllocRequest(UnlockedLoaderHeap * const pHeap, void* ptr, size_t dwSize)
+ {
+ FireEtwAllocRequest(pHeap, ptr, static_cast<unsigned int>(dwSize), 0, 0, GetClrInstanceId());
+ }
+#else
+#define EtwAllocRequest(pHeap, ptr, dwSize) ((void)0)
+#endif // SELF_NO_HOST
+}
+
+//
+// RangeLists are constructed so they can be searched from multiple
+// threads without locking. They do require locking in order to
+// be safely modified, though.
+//
+
+RangeList::RangeList()
+{
+ WRAPPER_NO_CONTRACT;
+
+ InitBlock(&m_starterBlock);
+
+ m_firstEmptyBlock = &m_starterBlock;
+ m_firstEmptyRange = 0;
+}
+
+RangeList::~RangeList()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ RangeListBlock *b = m_starterBlock.next;
+
+ while (b != NULL)
+ {
+ RangeListBlock *bNext = b->next;
+ delete b;
+ b = bNext;
+ }
+}
+
+void RangeList::InitBlock(RangeListBlock *b)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Range *r = b->ranges;
+ Range *rEnd = r + RANGE_COUNT;
+ while (r < rEnd)
+ r++->id = NULL;
+
+ b->next = NULL;
+}
+
+BOOL RangeList::AddRangeWorker(const BYTE *start, const BYTE *end, void *id)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END
+
+ _ASSERTE(id != NULL);
+
+ RangeListBlock *b = m_firstEmptyBlock;
+ Range *r = b->ranges + m_firstEmptyRange;
+ Range *rEnd = b->ranges + RANGE_COUNT;
+
+ while (TRUE)
+ {
+ while (r < rEnd)
+ {
+ if (r->id == NULL)
+ {
+ r->start = (TADDR)start;
+ r->end = (TADDR)end;
+ r->id = (TADDR)id;
+
+ r++;
+
+ m_firstEmptyBlock = b;
+ m_firstEmptyRange = r - b->ranges;
+
+ return TRUE;
+ }
+ r++;
+ }
+
+ //
+ // If there are no more blocks, allocate a
+ // new one.
+ //
+
+ if (b->next == NULL)
+ {
+ RangeListBlock *newBlock = new (nothrow) RangeListBlock;
+
+ if (newBlock == NULL)
+ {
+ m_firstEmptyBlock = b;
+ m_firstEmptyRange = r - b->ranges;
+ return FALSE;
+ }
+
+ InitBlock(newBlock);
+
+ newBlock->next = NULL;
+ b->next = newBlock;
+ }
+
+ //
+ // Next block
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+void RangeList::RemoveRangesWorker(void *id, const BYTE* start, const BYTE* end)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ RangeListBlock *b = &m_starterBlock;
+ Range *r = b->ranges;
+ Range *rEnd = r + RANGE_COUNT;
+
+ //
+ // Find the first free element, & mark it.
+ //
+
+ while (TRUE)
+ {
+ //
+ // Clear entries in this block.
+ //
+
+ while (r < rEnd)
+ {
+ if (r->id != NULL)
+ {
+ if (start != NULL)
+ {
+ _ASSERTE(end != NULL);
+
+ if (r->start >= (TADDR)start && r->start < (TADDR)end)
+ {
+ CONSISTENCY_CHECK_MSGF(r->end >= (TADDR)start &&
+ r->end <= (TADDR)end,
+ ("r: %p start: %p end: %p", r, start, end));
+ r->id = NULL;
+ }
+ }
+ else if (r->id == (TADDR)id)
+ {
+ r->id = NULL;
+ }
+ }
+
+ r++;
+ }
+
+ //
+ // If there are no more blocks, we're done.
+ //
+
+ if (b->next == NULL)
+ {
+ m_firstEmptyRange = 0;
+ m_firstEmptyBlock = &m_starterBlock;
+
+ return;
+ }
+
+ //
+ // Next block.
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL RangeList::IsInRangeWorker(TADDR address, TADDR *pID /* = NULL */)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ SUPPORTS_DAC;
+
+ RangeListBlock* b = &m_starterBlock;
+ Range* r = b->ranges;
+ Range* rEnd = r + RANGE_COUNT;
+
+ //
+ // Look for a matching element
+ //
+
+ while (TRUE)
+ {
+ while (r < rEnd)
+ {
+ if (r->id != NULL &&
+ address >= r->start
+ && address < r->end)
+ {
+ if (pID != NULL)
+ {
+ *pID = r->id;
+ }
+ return TRUE;
+ }
+ r++;
+ }
+
+ //
+ // If there are no more blocks, we're done.
+ //
+
+ if (b->next == NULL)
+ return FALSE;
+
+ //
+ // Next block.
+ //
+
+ b = b->next;
+ r = b->ranges;
+ rEnd = r + RANGE_COUNT;
+ }
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+RangeList::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ SUPPORTS_DAC;
+ WRAPPER_NO_CONTRACT;
+
+ // This class is almost always contained in something
+ // else so there's no enumeration of 'this'.
+
+ RangeListBlock* block = &m_starterBlock;
+ block->EnumMemoryRegions(flags);
+
+ while (block->next.IsValid())
+ {
+ block->next.EnumMem();
+ block = block->next;
+
+ block->EnumMemoryRegions(flags);
+ }
+}
+
+void
+RangeList::RangeListBlock::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ Range* range;
+ TADDR BADFOOD;
+ TSIZE_T size;
+ int i;
+
+ // The code below iterates each range stored in the RangeListBlock and
+ // dumps the memory region represented by each range.
+ // It is too much memory for a mini-dump, so we just bail out for mini-dumps.
+ if (flags == CLRDATA_ENUM_MEM_MINI || flags == CLRDATA_ENUM_MEM_TRIAGE)
+ {
+ return;
+ }
+
+ BIT64_ONLY( BADFOOD = 0xbaadf00dbaadf00d; );
+ NOT_BIT64( BADFOOD = 0xbaadf00d; );
+
+ for (i=0; i<RANGE_COUNT; i++)
+ {
+ range = &(this->ranges[i]);
+ if (range->id == NULL || range->start == NULL || range->end == NULL ||
+ // just looking at the lower 4bytes is good enough on WIN64
+ range->start == BADFOOD || range->end == BADFOOD)
+ {
+ break;
+ }
+
+ size = range->end - range->start;
+ _ASSERTE( size < UINT32_MAX ); // ranges should be less than 4gig!
+
+ // We can't be sure this entire range is mapped. For example, the code:StubLinkStubManager
+ // keeps track of all ranges in the code:BaseDomain::m_pStubHeap LoaderHeap, and
+ // code:LoaderHeap::UnlockedReservePages adds a range for the entire reserved region, instead
+ // of updating the RangeList when pages are committed. But in that case, the committed region of
+ // memory will be enumerated by the LoaderHeap anyway, so it's OK if this fails
+ DacEnumMemoryRegion(range->start, size, false);
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+//=====================================================================================
+// In DEBUG builds only, we tag live blocks with the requested size and the type of
+// allocation (AllocMem, AllocAlignedMem, AllocateOntoReservedMem). This is strictly
+// to validate that those who call Backout* are passing in the right values.
+//
+// For simplicity, we'll use one LoaderHeapValidationTag structure for all types even
+// though not all fields are applicable to all types.
+//=====================================================================================
+#ifdef _DEBUG
+enum AllocationType
+{
+ kAllocMem = 1,
+ kFreedMem = 4,
+};
+
+struct LoaderHeapValidationTag
+{
+ size_t m_dwRequestedSize; // What the caller requested (not what was actually allocated)
+ AllocationType m_allocationType; // Which api allocated this block.
+ const char * m_szFile; // Who allocated me
+ int m_lineNum; // Who allocated me
+
+};
+#endif //_DEBUG
+
+
+
+
+
+//=====================================================================================
+// These classes do detailed loaderheap sniffing to help in debugging heap crashes
+//=====================================================================================
+#ifdef _DEBUG
+
+// This structure logs the results of an Alloc or Free call. They are stored in reverse time order
+// with UnlockedLoaderHeap::m_pEventList pointing to the most recent event.
+struct LoaderHeapEvent
+{
+ LoaderHeapEvent *m_pNext;
+ AllocationType m_allocationType; //Which api was called
+ const char *m_szFile; //Caller Id
+ int m_lineNum; //Caller Id
+ const char *m_szAllocFile; //(BackoutEvents): Who allocated the block?
+ int m_allocLineNum; //(BackoutEvents): Who allocated the block?
+ void *m_pMem; //Starting address of block
+ size_t m_dwRequestedSize; //Requested size of block
+ size_t m_dwSize; //Actual size of block (including validation tags, padding, everything)
+
+
+ void Describe(SString *pSString)
+ {
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ DISABLED(NOTHROW);
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ pSString->AppendASCII("\n");
+
+ {
+ StackSString buf;
+ if (m_allocationType == kFreedMem)
+ {
+ buf.Printf(" Freed at: %s (line %d)\n", m_szFile, m_lineNum);
+ buf.Printf(" (block originally allocated at %s (line %d)\n", m_szAllocFile, m_allocLineNum);
+ }
+ else
+ {
+ buf.Printf(" Allocated at: %s (line %d)\n", m_szFile, m_lineNum);
+ }
+ pSString->Append(buf);
+ }
+
+ if (!QuietValidate())
+ {
+ pSString->AppendASCII(" *** THIS BLOCK HAS BEEN CORRUPTED ***\n");
+ }
+
+
+
+ {
+ StackSString buf;
+ buf.Printf(" Type: ");
+ switch (m_allocationType)
+ {
+ case kAllocMem:
+ buf.AppendASCII("AllocMem()\n");
+ break;
+ case kFreedMem:
+ buf.AppendASCII("Free\n");
+ break;
+ default:
+ break;
+ }
+ pSString->Append(buf);
+ }
+
+
+ {
+ StackSString buf;
+ buf.Printf(" Start of block: 0x%p\n", m_pMem);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" End of block: 0x%p\n", ((BYTE*)m_pMem) + m_dwSize - 1);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" Requested size: %lu (0x%lx)\n", (ULONG)m_dwRequestedSize, (ULONG)m_dwRequestedSize);
+ pSString->Append(buf);
+ }
+
+ {
+ StackSString buf;
+ buf.Printf(" Actual size: %lu (0x%lx)\n", (ULONG)m_dwSize, (ULONG)m_dwSize);
+ pSString->Append(buf);
+ }
+
+ pSString->AppendASCII("\n");
+ }
+
+
+
+ BOOL QuietValidate();
+
+};
+
+
+class LoaderHeapSniffer
+{
+ public:
+ static DWORD InitDebugFlags()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ DWORD dwDebugFlags = 0;
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LoaderHeapCallTracing))
+ {
+ dwDebugFlags |= UnlockedLoaderHeap::kCallTracing;
+ }
+ return dwDebugFlags;
+ }
+
+
+ static VOID RecordEvent(UnlockedLoaderHeap *pHeap,
+ AllocationType allocationType,
+ __in const char *szFile,
+ int lineNum,
+ __in const char *szAllocFile,
+ int allocLineNum,
+ void *pMem,
+ size_t dwRequestedSize,
+ size_t dwSize
+ );
+
+ static VOID ClearEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ LoaderHeapEvent *pNext = pEvent->m_pNext;
+ delete pEvent;
+ pEvent = pNext;
+ }
+ pHeap->m_pEventList = NULL;
+ }
+
+
+ static VOID CompactEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LoaderHeapEvent **ppEvent = &(pHeap->m_pEventList);
+ while (*ppEvent)
+ {
+ LoaderHeapEvent *pEvent = *ppEvent;
+ if (pEvent->m_allocationType != kFreedMem)
+ {
+ ppEvent = &(pEvent->m_pNext);
+ }
+ else
+ {
+ LoaderHeapEvent **ppWalk = &(pEvent->m_pNext);
+ BOOL fMatchFound = FALSE;
+ while (*ppWalk && !fMatchFound)
+ {
+ LoaderHeapEvent *pWalk = *ppWalk;
+ if (pWalk->m_allocationType != kFreedMem &&
+ pWalk->m_pMem == pEvent->m_pMem &&
+ pWalk->m_dwRequestedSize == pEvent->m_dwRequestedSize)
+ {
+ // Delete matched pairs
+
+ // Order is important here - updating *ppWalk may change pEvent->m_pNext, and we want
+ // to get the updated value when we unlink pEvent.
+ *ppWalk = pWalk->m_pNext;
+ *ppEvent = pEvent->m_pNext;
+
+ delete pEvent;
+ delete pWalk;
+ fMatchFound = TRUE;
+ }
+ else
+ {
+ ppWalk = &(pWalk->m_pNext);
+ }
+ }
+
+ if (!fMatchFound)
+ {
+ ppEvent = &(pEvent->m_pNext);
+ }
+ }
+ }
+ }
+ static VOID PrintEvents(UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ printf("\n------------- LoaderHeapEvents (in reverse time order!) --------------------");
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ printf("\n");
+ switch (pEvent->m_allocationType)
+ {
+ case kAllocMem: printf("AllocMem "); break;
+ case kFreedMem: printf("BackoutMem "); break;
+
+ }
+ printf(" ptr = 0x%-8p", pEvent->m_pMem);
+ printf(" rqsize = 0x%-8x", (DWORD)pEvent->m_dwRequestedSize);
+ printf(" actsize = 0x%-8x", (DWORD)pEvent->m_dwSize);
+ printf(" (at %s@%d)", pEvent->m_szFile, pEvent->m_lineNum);
+ if (pEvent->m_allocationType == kFreedMem)
+ {
+ printf(" (original allocation at %s@%d)", pEvent->m_szAllocFile, pEvent->m_allocLineNum);
+ }
+
+ pEvent = pEvent->m_pNext;
+
+ }
+ printf("\n------------- End of LoaderHeapEvents --------------------------------------");
+ printf("\n");
+
+ }
+
+
+ static VOID PitchSniffer(SString *pSString)
+ {
+ WRAPPER_NO_CONTRACT;
+ pSString->AppendASCII("\n"
+ "\nBecause call-tracing wasn't turned on, we couldn't provide details about who last owned the affected memory block. To get more precise diagnostics,"
+ "\nset the following registry DWORD value:"
+ "\n"
+ "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1"
+ "\n"
+ "\nand rerun the scenario that crashed."
+ "\n"
+ "\n");
+ }
+
+ static LoaderHeapEvent *FindEvent(UnlockedLoaderHeap *pHeap, void *pAddr)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ LoaderHeapEvent *pEvent = pHeap->m_pEventList;
+ while (pEvent)
+ {
+ if (pAddr >= pEvent->m_pMem && pAddr <= ( ((BYTE*)pEvent->m_pMem) + pEvent->m_dwSize - 1))
+ {
+ return pEvent;
+ }
+ pEvent = pEvent->m_pNext;
+ }
+ return NULL;
+
+ }
+
+
+ static void ValidateFreeList(UnlockedLoaderHeap *pHeap);
+
+ static void WeGotAFaultNowWhat(UnlockedLoaderHeap *pHeap)
+ {
+ WRAPPER_NO_CONTRACT;
+ ValidateFreeList(pHeap);
+
+ //If none of the above popped up an assert, pop up a generic one.
+ _ASSERTE(!("Unexpected AV inside LoaderHeap. The usual reason is that someone overwrote the end of a block or wrote into a freed block.\n"));
+
+ }
+
+};
+
+
+#endif
+
+
+#ifdef _DEBUG
+#define LOADER_HEAP_BEGIN_TRAP_FAULT BOOL __faulted = FALSE; EX_TRY {
+#define LOADER_HEAP_END_TRAP_FAULT } EX_CATCH {__faulted = TRUE; } EX_END_CATCH(SwallowAllExceptions) if (__faulted) LoaderHeapSniffer::WeGotAFaultNowWhat(pHeap);
+#else
+#define LOADER_HEAP_BEGIN_TRAP_FAULT
+#define LOADER_HEAP_END_TRAP_FAULT
+#endif
+
+
+size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap);
+
+//=====================================================================================
+// This freelist implementation is a first cut and probably needs to be tuned.
+// It should be tuned with the following assumptions:
+//
+// - Freeing LoaderHeap memory is done primarily for OOM backout. LoaderHeaps
+// weren't designed to be general purpose heaps and shouldn't be used that way.
+//
+// - And hence, when memory is freed, expect it to be freed in large clumps and in a
+// LIFO order. Since the LoaderHeap normally hands out memory with sequentially
+// increasing addresses, blocks will typically be freed with sequentially decreasing
+// addresses.
+//
+// The first cut of the freelist is a single-linked list of free blocks using first-fit.
+// Assuming the above alloc-free pattern holds, the list will end up mostly sorted
+// in increasing address order. When a block is freed, we'll attempt to coalesce it
+// with the first block in the list. We could also choose to be more aggressive about
+// sorting and coalescing but this should probably catch most cases in practice.
+//=====================================================================================
+
+// When a block is freed, we place this structure on the first bytes of the freed block (Allocations
+// are bumped in size if necessary to make sure there's room.)
+struct LoaderHeapFreeBlock
+{
+ public:
+ LoaderHeapFreeBlock *m_pNext; // Pointer to next block on free list
+ size_t m_dwSize; // Total size of this block (including this header)
+//! Try not to grow the size of this structure. It places a minimum size on LoaderHeap allocations.
+
+ static void InsertFreeBlock(LoaderHeapFreeBlock **ppHead, void *pMem, size_t dwTotalSize, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ // It's illegal to insert a free block that's smaller than the minimum sized allocation -
+ // it may stay stranded on the freelist forever.
+#ifdef _DEBUG
+ if (!(dwTotalSize >= AllocMem_TotalSize(1, pHeap)))
+ {
+ LoaderHeapSniffer::ValidateFreeList(pHeap);
+ _ASSERTE(dwTotalSize >= AllocMem_TotalSize(1, pHeap));
+ }
+
+ if (!(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT)))
+ {
+ LoaderHeapSniffer::ValidateFreeList(pHeap);
+ _ASSERTE(0 == (dwTotalSize & ALLOC_ALIGN_CONSTANT));
+ }
+#endif
+
+ INDEBUG(memset(pMem, 0xcc, dwTotalSize);)
+ LoaderHeapFreeBlock *pNewBlock = (LoaderHeapFreeBlock*)pMem;
+ pNewBlock->m_pNext = *ppHead;
+ pNewBlock->m_dwSize = dwTotalSize;
+ *ppHead = pNewBlock;
+
+ MergeBlock(pNewBlock, pHeap);
+
+ LOADER_HEAP_END_TRAP_FAULT
+ }
+
+
+ static void *AllocFromFreeList(LoaderHeapFreeBlock **ppHead, size_t dwSize, BOOL fRemoveFromFreeList, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ INCONTRACT(_ASSERTE_IMPL(!ARE_FAULTS_FORBIDDEN()));
+
+ void *pResult = NULL;
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ LoaderHeapFreeBlock **ppWalk = ppHead;
+ while (*ppWalk)
+ {
+ LoaderHeapFreeBlock *pCur = *ppWalk;
+ size_t dwCurSize = pCur->m_dwSize;
+ if (dwCurSize == dwSize)
+ {
+ pResult = pCur;
+ // Exact match. Hooray!
+ if (fRemoveFromFreeList)
+ {
+ *ppWalk = pCur->m_pNext;
+ }
+ break;
+ }
+ else if (dwCurSize > dwSize && (dwCurSize - dwSize) >= AllocMem_TotalSize(1, pHeap))
+ {
+ // Partial match. Ok...
+ pResult = pCur;
+ if (fRemoveFromFreeList)
+ {
+ *ppWalk = pCur->m_pNext;
+ InsertFreeBlock(ppWalk, ((BYTE*)pCur) + dwSize, dwCurSize - dwSize, pHeap );
+ }
+ break;
+ }
+
+ // Either block is too small or splitting the block would leave a remainder that's smaller than
+ // the minimum block size. Onto next one.
+
+ ppWalk = &( pCur->m_pNext );
+ }
+
+ if (pResult && fRemoveFromFreeList)
+ {
+ // Callers of loaderheap assume allocated memory is zero-inited so we must preserve this invariant!
+ memset(pResult, 0, dwSize);
+ }
+ LOADER_HEAP_END_TRAP_FAULT
+ return pResult;
+
+
+
+ }
+
+
+ private:
+ // Try to merge pFreeBlock with its immediate successor. Return TRUE if a merge happened. FALSE if no merge happened.
+ static BOOL MergeBlock(LoaderHeapFreeBlock *pFreeBlock, UnlockedLoaderHeap *pHeap)
+ {
+ STATIC_CONTRACT_NOTHROW;
+
+ BOOL result = FALSE;
+
+ LOADER_HEAP_BEGIN_TRAP_FAULT
+
+ LoaderHeapFreeBlock *pNextBlock = pFreeBlock->m_pNext;
+ size_t dwSize = pFreeBlock->m_dwSize;
+
+ if (pNextBlock == NULL || ((BYTE*)pNextBlock) != (((BYTE*)pFreeBlock) + dwSize))
+ {
+ result = FALSE;
+ }
+ else
+ {
+ size_t dwCombinedSize = dwSize + pNextBlock->m_dwSize;
+ LoaderHeapFreeBlock *pNextNextBlock = pNextBlock->m_pNext;
+ INDEBUG(memset(pFreeBlock, 0xcc, dwCombinedSize);)
+ pFreeBlock->m_pNext = pNextNextBlock;
+ pFreeBlock->m_dwSize = dwCombinedSize;
+
+ result = TRUE;
+ }
+
+ LOADER_HEAP_END_TRAP_FAULT
+ return result;
+
+ }
+
+};
+
+
+
+
+//=====================================================================================
+// These helpers encapsulate the actual layout of a block allocated by AllocMem
+// and UnlockedAllocMem():
+//
+// ==> Starting address is always pointer-aligned.
+//
+// - x bytes of user bytes (where "x" is the actual dwSize passed into AllocMem)
+//
+// - y bytes of "EE" (DEBUG-ONLY) (where "y" == LOADER_HEAP_DEBUG_BOUNDARY (normally 0))
+// - z bytes of pad (DEBUG-ONLY) (where "z" is just enough to pointer-align the following byte)
+// - a bytes of tag (DEBUG-ONLY) (where "a" is sizeof(LoaderHeapValidationTag)
+//
+// - b bytes of pad (if total size after all this < sizeof(LoaderHeapFreeBlock), pad enough to make it the size of LoaderHeapFreeBlock)
+// - c bytes of pad (where "c" is just enough to pointer-align the following byte)
+//
+// ==> Following address is always pointer-aligned
+//=====================================================================================
+
+// Convert the requested size into the total # of bytes we'll actually allocate (including padding)
+inline size_t AllocMem_TotalSize(size_t dwRequestedSize, UnlockedLoaderHeap *pHeap)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t dwSize = dwRequestedSize;
+#ifdef _DEBUG
+ dwSize += LOADER_HEAP_DEBUG_BOUNDARY;
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+#endif
+
+ if (!pHeap->m_fExplicitControl)
+ {
+#ifdef _DEBUG
+ dwSize += sizeof(LoaderHeapValidationTag);
+#endif
+ if (dwSize < sizeof(LoaderHeapFreeBlock))
+ {
+ dwSize = sizeof(LoaderHeapFreeBlock);
+ }
+ }
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+
+ return dwSize;
+}
+
+
+#ifdef _DEBUG
+LoaderHeapValidationTag *AllocMem_GetTag(LPVOID pBlock, size_t dwRequestedSize)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ size_t dwSize = dwRequestedSize;
+ dwSize += LOADER_HEAP_DEBUG_BOUNDARY;
+ dwSize = ((dwSize + ALLOC_ALIGN_CONSTANT) & (~ALLOC_ALIGN_CONSTANT));
+ return (LoaderHeapValidationTag *)( ((BYTE*)pBlock) + dwSize );
+}
+#endif
+
+
+
+
+
+//=====================================================================================
+// UnlockedLoaderHeap methods
+//=====================================================================================
+
+#ifndef DACCESS_COMPILE
+
+UnlockedLoaderHeap::UnlockedLoaderHeap(DWORD dwReserveBlockSize,
+ DWORD dwCommitBlockSize,
+ const BYTE* dwReservedRegionAddress,
+ SIZE_T dwReservedRegionSize,
+ RangeList *pRangeList,
+ BOOL fMakeExecutable)
+{
+ CONTRACTL
+ {
+ CONSTRUCTOR_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ m_pCurBlock = NULL;
+ m_pFirstBlock = NULL;
+
+ m_dwReserveBlockSize = dwReserveBlockSize;
+ m_dwCommitBlockSize = dwCommitBlockSize;
+
+ m_pPtrToEndOfCommittedRegion = NULL;
+ m_pEndReservedRegion = NULL;
+ m_pAllocPtr = NULL;
+
+ m_pRangeList = pRangeList;
+
+ // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY
+ m_dwTotalAlloc = 0;
+
+#ifdef _DEBUG
+ m_dwDebugWastedBytes = 0;
+ s_dwNumInstancesOfLoaderHeaps++;
+ m_pEventList = NULL;
+ m_dwDebugFlags = LoaderHeapSniffer::InitDebugFlags();
+ m_fPermitStubsWithUnwindInfo = FALSE;
+ m_fStubUnwindInfoUnregistered= FALSE;
+#endif
+
+ m_Options = 0;
+
+#ifndef CROSSGEN_COMPILE
+ if (fMakeExecutable)
+ m_Options |= LHF_EXECUTABLE;
+#endif // CROSSGEN_COMPILE
+
+ m_pFirstFreeBlock = NULL;
+
+ if (dwReservedRegionAddress != NULL && dwReservedRegionSize > 0)
+ {
+ m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, FALSE);
+ }
+}
+
+// ~LoaderHeap is not synchronised (obviously)
+UnlockedLoaderHeap::~UnlockedLoaderHeap()
+{
+ CONTRACTL
+ {
+ DESTRUCTOR_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ _ASSERTE(!m_fPermitStubsWithUnwindInfo || m_fStubUnwindInfoUnregistered);
+
+ if (m_pRangeList != NULL)
+ m_pRangeList->RemoveRanges((void *) this);
+
+ LoaderHeapBlock *pSearch, *pNext;
+
+ for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext)
+ {
+ void * pVirtualAddress;
+ BOOL fReleaseMemory;
+
+ pVirtualAddress = pSearch->pVirtualAddress;
+ fReleaseMemory = pSearch->m_fReleaseMemory;
+ pNext = pSearch->pNext;
+
+ if (fReleaseMemory)
+ {
+ BOOL fSuccess;
+ fSuccess = ClrVirtualFree(pVirtualAddress, 0, MEM_RELEASE);
+ _ASSERTE(fSuccess);
+ }
+ }
+
+ if (m_reservedBlock.m_fReleaseMemory)
+ {
+ BOOL fSuccess;
+ fSuccess = ClrVirtualFree(m_reservedBlock.pVirtualAddress, 0, MEM_RELEASE);
+ _ASSERTE(fSuccess);
+ }
+
+ INDEBUG(s_dwNumInstancesOfLoaderHeaps --;)
+}
+
+void UnlockedLoaderHeap::UnlockedSetReservedRegion(BYTE* dwReservedRegionAddress, SIZE_T dwReservedRegionSize, BOOL fReleaseMemory)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERTE(m_reservedBlock.pVirtualAddress == NULL);
+ m_reservedBlock.Init((void *)dwReservedRegionAddress, dwReservedRegionSize, fReleaseMemory);
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+#if 0
+// Disables access to all pages in the heap - useful when trying to determine if someone is
+// accessing something in the low frequency heap
+void UnlockedLoaderHeap::DebugGuardHeap()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapBlock *pSearch, *pNext;
+
+ for (pSearch = m_pFirstBlock; pSearch; pSearch = pNext)
+ {
+ void * pResult;
+ void * pVirtualAddress;
+
+ pVirtualAddress = pSearch->pVirtualAddress;
+ pNext = pSearch->pNext;
+
+ pResult = ClrVirtualAlloc(pVirtualAddress, pSearch->dwVirtualSize, MEM_COMMIT, PAGE_NOACCESS);
+ _ASSERTE(pResult != NULL);
+ }
+}
+#endif
+
+size_t UnlockedLoaderHeap::GetBytesAvailCommittedRegion()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pAllocPtr < m_pPtrToEndOfCommittedRegion)
+ return (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr);
+ else
+ return 0;
+}
+
+size_t UnlockedLoaderHeap::GetBytesAvailReservedRegion()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_pAllocPtr < m_pEndReservedRegion)
+ return (size_t)(m_pEndReservedRegion- m_pAllocPtr);
+ else
+ return 0;
+}
+
+#define SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve) \
+ m_pPtrToEndOfCommittedRegion = (BYTE *) (pData) + (dwSizeToCommit); \
+ m_pAllocPtr = (BYTE *) (pData) + sizeof(LoaderHeapBlock); \
+ m_pEndReservedRegion = (BYTE *) (pData) + (dwSizeToReserve);
+
+
+#ifndef DACCESS_COMPILE
+
+BOOL UnlockedLoaderHeap::UnlockedReservePages(size_t dwSizeToCommit)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ size_t dwSizeToReserve;
+
+ // Add sizeof(LoaderHeapBlock)
+ dwSizeToCommit += sizeof(LoaderHeapBlock);
+
+ // Round to page size again
+ dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize());
+
+ void *pData = NULL;
+ BOOL fReleaseMemory = TRUE;
+
+ // We were provided with a reserved memory block at instance creation time, so use it if it's big enough.
+ if (m_reservedBlock.pVirtualAddress != NULL &&
+ m_reservedBlock.dwVirtualSize >= dwSizeToCommit)
+ {
+ // Get the info out of the block.
+ pData = m_reservedBlock.pVirtualAddress;
+ dwSizeToReserve = m_reservedBlock.dwVirtualSize;
+ fReleaseMemory = m_reservedBlock.m_fReleaseMemory;
+
+ // Zero the block so this memory doesn't get used again.
+ m_reservedBlock.Init(NULL, 0, FALSE);
+ }
+ // The caller is asking us to allocate the memory
+ else
+ {
+ if (m_fExplicitControl)
+ {
+ return FALSE;
+ }
+
+ // Figure out how much to reserve
+ dwSizeToReserve = max(dwSizeToCommit, m_dwReserveBlockSize);
+
+ // Round to VIRTUAL_ALLOC_RESERVE_GRANULARITY
+ dwSizeToReserve = ALIGN_UP(dwSizeToReserve, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+
+ _ASSERTE(dwSizeToCommit <= dwSizeToReserve);
+
+ //
+ // Reserve pages
+ //
+
+ pData = ClrVirtualAllocExecutable(dwSizeToReserve, MEM_RESERVE, PAGE_NOACCESS);
+ if (pData == NULL)
+ {
+ return FALSE;
+ }
+ }
+
+ // When the user passes in the reserved memory, the commit size is 0 and is adjusted to be the sizeof(LoaderHeap).
+ // If for some reason this is not true then we just catch this via an assertion and the dev who changed code
+ // would have to add logic here to handle the case when committed mem is more than the reserved mem. One option
+ // could be to leak the users memory and reserve+commit a new block, Another option would be to fail the alloc mem
+ // and notify the user to provide more reserved mem.
+ _ASSERTE((dwSizeToCommit <= dwSizeToReserve) && "Loaderheap tried to commit more memory than reserved by user");
+
+ if (pData == NULL)
+ {
+ //_ASSERTE(!"Unable to ClrVirtualAlloc reserve in a loaderheap");
+ return FALSE;
+ }
+
+ // Commit first set of pages, since it will contain the LoaderHeapBlock
+ void *pTemp = ClrVirtualAlloc(pData, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ if (pTemp == NULL)
+ {
+ //_ASSERTE(!"Unable to ClrVirtualAlloc commit in a loaderheap");
+
+ // Unable to commit - release pages
+ if (fReleaseMemory)
+ ClrVirtualFree(pData, 0, MEM_RELEASE);
+
+ return FALSE;
+ }
+
+ // Record reserved range in range list, if one is specified
+ // Do this AFTER the commit - otherwise we'll have bogus ranges included.
+ if (m_pRangeList != NULL)
+ {
+ if (!m_pRangeList->AddRange((const BYTE *) pData,
+ ((const BYTE *) pData) + dwSizeToReserve,
+ (void *) this))
+ {
+
+ if (fReleaseMemory)
+ ClrVirtualFree(pData, 0, MEM_RELEASE);
+
+ return FALSE;
+ }
+ }
+
+ m_dwTotalAlloc += dwSizeToCommit;
+
+ LoaderHeapBlock *pNewBlock;
+
+#if defined(HOST_OSX) && defined(HOST_ARM64)
+ // Always assume we are touching executable heap
+ auto jitWriteEnableHolder = PAL_JITWriteEnable(true);
+#endif // defined(HOST_OSX) && defined(HOST_ARM64)
+
+ pNewBlock = (LoaderHeapBlock *) pData;
+
+ pNewBlock->dwVirtualSize = dwSizeToReserve;
+ pNewBlock->pVirtualAddress = pData;
+ pNewBlock->pNext = NULL;
+ pNewBlock->m_fReleaseMemory = fReleaseMemory;
+
+ LoaderHeapBlock *pCurBlock = m_pCurBlock;
+
+ // Add to linked list
+ while (pCurBlock != NULL &&
+ pCurBlock->pNext != NULL)
+ pCurBlock = pCurBlock->pNext;
+
+ if (pCurBlock != NULL)
+ m_pCurBlock->pNext = pNewBlock;
+ else
+ m_pFirstBlock = pNewBlock;
+
+ // If we want to use the memory immediately...
+ m_pCurBlock = pNewBlock;
+
+ SETUP_NEW_BLOCK(pData, dwSizeToCommit, dwSizeToReserve);
+
+ return TRUE;
+}
+
+// Get some more committed pages - either commit some more in the current reserved region, or, if it
+// has run out, reserve another set of pages.
+// Returns: FALSE if we can't get any more memory
+// TRUE: We can/did get some more memory - check to see if it's sufficient for
+// the caller's needs (see UnlockedAllocMem for example of use)
+BOOL UnlockedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END;
+
+ // If we have memory we can use, what are you doing here!
+ _ASSERTE(dwMinSize > (SIZE_T)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr));
+
+ // Does this fit in the reserved region?
+ if (dwMinSize <= (size_t)(m_pEndReservedRegion - m_pAllocPtr))
+ {
+ SIZE_T dwSizeToCommit = (m_pAllocPtr + dwMinSize) - m_pPtrToEndOfCommittedRegion;
+
+ if (dwSizeToCommit < m_dwCommitBlockSize)
+ dwSizeToCommit = min((SIZE_T)(m_pEndReservedRegion - m_pPtrToEndOfCommittedRegion), (SIZE_T)m_dwCommitBlockSize);
+
+ // Round to page size
+ dwSizeToCommit = ALIGN_UP(dwSizeToCommit, GetOsPageSize());
+
+ // Yes, so commit the desired number of reserved pages
+ void *pData = ClrVirtualAlloc(m_pPtrToEndOfCommittedRegion, dwSizeToCommit, MEM_COMMIT, (m_Options & LHF_EXECUTABLE) ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ if (pData == NULL)
+ return FALSE;
+
+ m_dwTotalAlloc += dwSizeToCommit;
+
+ m_pPtrToEndOfCommittedRegion += dwSizeToCommit;
+ return TRUE;
+ }
+
+ // Need to allocate a new set of reserved pages
+ INDEBUG(m_dwDebugWastedBytes += (size_t)(m_pPtrToEndOfCommittedRegion - m_pAllocPtr);)
+
+ // Note, there are unused reserved pages at end of current region -can't do much about that
+ // Provide dwMinSize here since UnlockedReservePages will round up the commit size again
+ // after adding in the size of the LoaderHeapBlock header.
+ return UnlockedReservePages(dwMinSize);
+}
+
+void *UnlockedLoaderHeap::UnlockedAllocMem(size_t dwSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ INJECT_FAULT(ThrowOutOfMemory(););
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ void *pResult = UnlockedAllocMem_NoThrow(
+ dwSize COMMA_INDEBUG(szFile) COMMA_INDEBUG(lineNum));
+
+ if (pResult == NULL)
+ ThrowOutOfMemory();
+
+ RETURN pResult;
+}
+
+#ifdef _DEBUG
+static DWORD ShouldInjectFault()
+{
+ static DWORD fInjectFault = 99;
+
+ if (fInjectFault == 99)
+ fInjectFault = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) != 0);
+ return fInjectFault;
+}
+
+#define SHOULD_INJECT_FAULT(return_statement) \
+ do { \
+ if (ShouldInjectFault() & 0x1) \
+ { \
+ char *a = new (nothrow) char; \
+ if (a == NULL) \
+ { \
+ return_statement; \
+ } \
+ delete a; \
+ } \
+ } while (FALSE)
+
+#else
+
+#define SHOULD_INJECT_FAULT(return_statement) do { (void)((void *)0); } while (FALSE)
+
+#endif
+
+void *UnlockedLoaderHeap::UnlockedAllocMem_NoThrow(size_t dwSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(CONTRACT_RETURN NULL;);
+ PRECONDITION(dwSize != 0);
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ SHOULD_INJECT_FAULT(RETURN NULL);
+
+ INDEBUG(size_t dwRequestedSize = dwSize;)
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+#ifdef RANDOMIZE_ALLOC
+ if (!m_fExplicitControl)
+ dwSize += s_random.Next() % 256;
+#endif
+
+ dwSize = AllocMem_TotalSize(dwSize, this);
+
+again:
+
+ {
+ // Any memory available on the free list?
+ void *pData = LoaderHeapFreeBlock::AllocFromFreeList(&m_pFirstFreeBlock, dwSize, TRUE /*fRemoveFromFreeList*/, this);
+ if (!pData)
+ {
+ // Enough bytes available in committed region?
+ if (dwSize <= GetBytesAvailCommittedRegion())
+ {
+ pData = m_pAllocPtr;
+ m_pAllocPtr += dwSize;
+ }
+ }
+
+ if (pData)
+ {
+#ifdef _DEBUG
+
+ BYTE *pAllocatedBytes = (BYTE *)pData;
+#if LOADER_HEAP_DEBUG_BOUNDARY > 0
+ // Don't fill the memory we allocated - it is assumed to be zeroed - fill the memory after it
+ memset(pAllocatedBytes + dwRequestedSize, 0xEE, LOADER_HEAP_DEBUG_BOUNDARY);
+#endif
+ if (dwRequestedSize > 0)
+ {
+ _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
+ "LoaderHeap must return zero-initialized memory");
+ }
+
+ if (!m_fExplicitControl)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(pData, dwRequestedSize);
+ pTag->m_allocationType = kAllocMem;
+ pTag->m_dwRequestedSize = dwRequestedSize;
+ pTag->m_szFile = szFile;
+ pTag->m_lineNum = lineNum;
+ }
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ LoaderHeapSniffer::RecordEvent(this,
+ kAllocMem,
+ szFile,
+ lineNum,
+ szFile,
+ lineNum,
+ pData,
+ dwRequestedSize,
+ dwSize
+ );
+ }
+
+#endif
+
+ EtwAllocRequest(this, pData, dwSize);
+ RETURN pData;
+ }
+ }
+
+ // Need to commit some more pages in reserved region.
+ // If we run out of pages in the reserved region, ClrVirtualAlloc some more pages
+ if (GetMoreCommittedPages(dwSize))
+ goto again;
+
+ // We could not satisfy this allocation request
+ RETURN NULL;
+}
+
+void UnlockedLoaderHeap::UnlockedBackoutMem(void *pMem,
+ size_t dwRequestedSize
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum)
+ COMMA_INDEBUG(__in const char *szAllocFile)
+ COMMA_INDEBUG(int allocLineNum))
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ // Because the primary use of this function is backout, we'll be nice and
+ // define Backout(NULL) be a legal NOP.
+ if (pMem == NULL)
+ {
+ return;
+ }
+
+#ifdef _DEBUG
+ {
+ DEBUG_ONLY_REGION();
+
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(pMem, dwRequestedSize);
+
+ if (pTag->m_dwRequestedSize != dwRequestedSize || pTag->m_allocationType != kAllocMem)
+ {
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation); // We're reporting a heap corruption - who cares about violations
+
+ StackSString message;
+ message.Printf("HEAP VIOLATION: Invalid BackoutMem() call made at:\n"
+ "\n"
+ " File: %s\n"
+ " Line: %d\n"
+ "\n"
+ "Attempting to free block originally allocated at:\n"
+ "\n"
+ " File: %s\n"
+ " Line: %d\n"
+ "\n"
+ "The arguments to BackoutMem() were:\n"
+ "\n"
+ " Pointer: 0x%p\n"
+ " Size: %lu (0x%lx)\n"
+ "\n"
+ ,szFile
+ ,lineNum
+ ,szAllocFile
+ ,allocLineNum
+ ,pMem
+ ,(ULONG)dwRequestedSize
+ ,(ULONG)dwRequestedSize
+ );
+
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ message.AppendASCII("*** CALLTRACING ENABLED ***\n");
+ LoaderHeapEvent *pEvent = LoaderHeapSniffer::FindEvent(this, pMem);
+ if (!pEvent)
+ {
+ message.AppendASCII("This pointer doesn't appear to have come from this LoaderHeap.\n");
+ }
+ else
+ {
+ message.AppendASCII(pMem == pEvent->m_pMem ? "We have the following data about this pointer:" : "This pointer points to the middle of the following block:");
+ pEvent->Describe(&message);
+ }
+ }
+
+ if (pTag->m_dwRequestedSize != dwRequestedSize)
+ {
+ StackSString buf;
+ buf.Printf(
+ "Possible causes:\n"
+ "\n"
+ " - This pointer wasn't allocated from this loaderheap.\n"
+ " - This pointer was allocated by AllocAlignedMem and you didn't adjust for the \"extra.\"\n"
+ " - This pointer has already been freed.\n"
+ " - You passed in the wrong size. You must pass the exact same size you passed to AllocMem().\n"
+ " - Someone wrote past the end of this block making it appear as if one of the above were true.\n"
+ );
+ message.Append(buf);
+
+ }
+ else
+ {
+ message.AppendASCII("This memory block is completely unrecognizable.\n");
+ }
+
+
+ if (!(m_dwDebugFlags & kCallTracing))
+ {
+ LoaderHeapSniffer::PitchSniffer(&message);
+ }
+
+ StackScratchBuffer scratch;
+ DbgAssertDialog(szFile, lineNum, (char*) message.GetANSI(scratch));
+
+ }
+ }
+#endif
+
+ size_t dwSize = AllocMem_TotalSize(dwRequestedSize, this);
+
+#ifdef _DEBUG
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ DEBUG_ONLY_REGION();
+
+ LoaderHeapValidationTag *pTag = m_fExplicitControl ? NULL : AllocMem_GetTag(pMem, dwRequestedSize);
+
+
+ LoaderHeapSniffer::RecordEvent(this,
+ kFreedMem,
+ szFile,
+ lineNum,
+ (pTag && (allocLineNum < 0)) ? pTag->m_szFile : szAllocFile,
+ (pTag && (allocLineNum < 0)) ? pTag->m_lineNum : allocLineNum,
+ pMem,
+ dwRequestedSize,
+ dwSize
+ );
+ }
+#endif
+
+ if (m_pAllocPtr == ( ((BYTE*)pMem) + dwSize ))
+ {
+ // Cool. This was the last block allocated. We can just undo the allocation instead
+ // of going to the freelist.
+ memset(pMem, 0x00, dwSize); // Fill freed region with 0
+ m_pAllocPtr = (BYTE*)pMem;
+ }
+ else
+ {
+ LoaderHeapFreeBlock::InsertFreeBlock(&m_pFirstFreeBlock, pMem, dwSize, this);
+ }
+
+}
+
+
+// Allocates memory aligned on power-of-2 boundary.
+//
+// The return value is a pointer that's guaranteed to be aligned.
+//
+// FREEING THIS BLOCK: Underneath, the actual block allocated may
+// be larger and start at an address prior to the one you got back.
+// It is this adjusted size and pointer that you pass to BackoutMem.
+// The required adjustment is passed back thru the pdwExtra pointer.
+//
+// Here is how to properly backout the memory:
+//
+// size_t dwExtra;
+// void *pMem = UnlockedAllocAlignedMem(dwRequestedSize, alignment, &dwExtra);
+// _ASSERTE( 0 == (pMem & (alignment - 1)) );
+// UnlockedBackoutMem( ((BYTE*)pMem) - dExtra, dwRequestedSize + dwExtra );
+//
+// If you use the AllocMemHolder or AllocMemTracker, all this is taken care of
+// behind the scenes.
+//
+//
+void *UnlockedLoaderHeap::UnlockedAllocAlignedMem_NoThrow(size_t dwRequestedSize,
+ size_t alignment,
+ size_t *pdwExtra
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACT(void*)
+ {
+ NOTHROW;
+
+ // Macro syntax can't handle this INJECT_FAULT expression - we'll use a precondition instead
+ //INJECT_FAULT( do{ if (*pdwExtra) {*pdwExtra = 0} RETURN NULL; } while(0) );
+
+ PRECONDITION( alignment != 0 );
+ PRECONDITION(0 == (alignment & (alignment - 1))); // require power of 2
+ POSTCONDITION( (RETVAL) ?
+ (0 == ( ((UINT_PTR)(RETVAL)) & (alignment - 1))) : // If non-null, pointer must be aligned
+ (pdwExtra == NULL || 0 == *pdwExtra) // or else *pdwExtra must be set to 0
+ );
+ }
+ CONTRACT_END
+
+ STATIC_CONTRACT_FAULT;
+
+ // Set default value
+ if (pdwExtra)
+ {
+ *pdwExtra = 0;
+ }
+
+ SHOULD_INJECT_FAULT(RETURN NULL);
+
+ void *pResult;
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ // Check for overflow if we align the allocation
+ if (dwRequestedSize + alignment < dwRequestedSize)
+ {
+ RETURN NULL;
+ }
+
+ // We don't know how much "extra" we need to satisfy the alignment until we know
+ // which address will be handed out which in turn we don't know because we don't
+ // know whether the allocation will fit within the current reserved range.
+ //
+ // Thus, we'll request as much heap growth as is needed for the worst case (extra == alignment)
+ size_t dwRoomSize = AllocMem_TotalSize(dwRequestedSize + alignment, this);
+ if (dwRoomSize > GetBytesAvailCommittedRegion())
+ {
+ if (!GetMoreCommittedPages(dwRoomSize))
+ {
+ RETURN NULL;
+ }
+ }
+
+ pResult = m_pAllocPtr;
+
+ size_t extra = alignment - ((size_t)pResult & ((size_t)alignment - 1));
+
+// On DEBUG, we force a non-zero extra so people don't forget to adjust for it on backout
+#ifndef _DEBUG
+ if (extra == alignment)
+ {
+ extra = 0;
+ }
+#endif
+
+ S_SIZE_T cbAllocSize = S_SIZE_T( dwRequestedSize ) + S_SIZE_T( extra );
+ if( cbAllocSize.IsOverflow() )
+ {
+ RETURN NULL;
+ }
+
+ size_t dwSize = AllocMem_TotalSize( cbAllocSize.Value(), this);
+ m_pAllocPtr += dwSize;
+
+
+ ((BYTE*&)pResult) += extra;
+
+#ifdef _DEBUG
+ BYTE *pAllocatedBytes = (BYTE *)pResult;
+#if LOADER_HEAP_DEBUG_BOUNDARY > 0
+ // Don't fill the entire memory - we assume it is all zeroed -just the memory after our alloc
+ memset(pAllocatedBytes + dwRequestedSize, 0xee, LOADER_HEAP_DEBUG_BOUNDARY);
+#endif
+
+ if (dwRequestedSize != 0)
+ {
+ _ASSERTE_MSG(pAllocatedBytes[0] == 0 && memcmp(pAllocatedBytes, pAllocatedBytes + 1, dwRequestedSize - 1) == 0,
+ "LoaderHeap must return zero-initialized memory");
+ }
+
+ if (m_dwDebugFlags & kCallTracing)
+ {
+ LoaderHeapSniffer::RecordEvent(this,
+ kAllocMem,
+ szFile,
+ lineNum,
+ szFile,
+ lineNum,
+ ((BYTE*)pResult) - extra,
+ dwRequestedSize + extra,
+ dwSize
+ );
+ }
+
+ EtwAllocRequest(this, pResult, dwSize);
+
+ if (!m_fExplicitControl)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(((BYTE*)pResult) - extra, dwRequestedSize + extra);
+ pTag->m_allocationType = kAllocMem;
+ pTag->m_dwRequestedSize = dwRequestedSize + extra;
+ pTag->m_szFile = szFile;
+ pTag->m_lineNum = lineNum;
+ }
+#endif //_DEBUG
+
+ if (pdwExtra)
+ {
+ *pdwExtra = extra;
+ }
+
+ RETURN pResult;
+
+}
+
+
+
+void *UnlockedLoaderHeap::UnlockedAllocAlignedMem(size_t dwRequestedSize,
+ size_t dwAlignment,
+ size_t *pdwExtra
+ COMMA_INDEBUG(__in const char *szFile)
+ COMMA_INDEBUG(int lineNum))
+{
+ CONTRACTL
+ {
+ THROWS;
+ INJECT_FAULT(ThrowOutOfMemory());
+ }
+ CONTRACTL_END
+
+ void *pResult = UnlockedAllocAlignedMem_NoThrow(dwRequestedSize,
+ dwAlignment,
+ pdwExtra
+ COMMA_INDEBUG(szFile)
+ COMMA_INDEBUG(lineNum));
+
+ if (!pResult)
+ {
+ ThrowOutOfMemory();
+ }
+
+ return pResult;
+
+
+}
+
+
+
+void *UnlockedLoaderHeap::UnlockedAllocMemForCode_NoThrow(size_t dwHeaderSize, size_t dwCodeSize, DWORD dwCodeAlignment, size_t dwReserveForJumpStubs)
+{
+ CONTRACT(void*)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ INJECT_FAULT(CONTRACT_RETURN NULL;);
+ PRECONDITION(0 == (dwCodeAlignment & (dwCodeAlignment - 1))); // require power of 2
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ _ASSERTE(m_fExplicitControl);
+
+ INCONTRACT(_ASSERTE(!ARE_FAULTS_FORBIDDEN()));
+
+ // We don't know how much "extra" we need to satisfy the alignment until we know
+ // which address will be handed out which in turn we don't know because we don't
+ // know whether the allocation will fit within the current reserved range.
+ //
+ // Thus, we'll request as much heap growth as is needed for the worst case (we request an extra dwCodeAlignment - 1 bytes)
+
+ S_SIZE_T cbAllocSize = S_SIZE_T(dwHeaderSize) + S_SIZE_T(dwCodeSize) + S_SIZE_T(dwCodeAlignment - 1) + S_SIZE_T(dwReserveForJumpStubs);
+ if( cbAllocSize.IsOverflow() )
+ {
+ RETURN NULL;
+ }
+
+ if (cbAllocSize.Value() > GetBytesAvailCommittedRegion())
+ {
+ if (GetMoreCommittedPages(cbAllocSize.Value()) == FALSE)
+ {
+ RETURN NULL;
+ }
+ }
+
+ BYTE *pResult = (BYTE *)ALIGN_UP(m_pAllocPtr + dwHeaderSize, dwCodeAlignment);
+ EtwAllocRequest(this, pResult, (pResult + dwCodeSize) - m_pAllocPtr);
+ m_pAllocPtr = pResult + dwCodeSize;
+
+ RETURN pResult;
+}
+
+
+#endif // #ifndef DACCESS_COMPILE
+
+BOOL UnlockedLoaderHeap::IsExecutable()
+{
+ return (m_Options & LHF_EXECUTABLE);
+}
+
+#ifdef DACCESS_COMPILE
+
+void UnlockedLoaderHeap::EnumMemoryRegions(CLRDataEnumMemoryFlags flags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ DAC_ENUM_DTHIS();
+
+ PTR_LoaderHeapBlock block = m_pFirstBlock;
+ while (block.IsValid())
+ {
+ // All we know is the virtual size of this block. We don't have any way to tell how
+ // much of this space was actually comitted, so don't expect that this will always
+ // succeed.
+ // @dbgtodo : Ideally we'd reduce the risk of corruption causing problems here.
+ // We could extend LoaderHeapBlock to track a commit size,
+ // but it seems wasteful (eg. makes each AppDomain objects 32 bytes larger on x64).
+ TADDR addr = dac_cast<TADDR>(block->pVirtualAddress);
+ TSIZE_T size = block->dwVirtualSize;
+ DacEnumMemoryRegion(addr, size, false);
+
+ block = block->pNext;
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+
+void UnlockedLoaderHeap::EnumPageRegions (EnumPageRegionsCallback *pCallback, PTR_VOID pvArgs)
+{
+ WRAPPER_NO_CONTRACT;
+
+ PTR_LoaderHeapBlock block = m_pFirstBlock;
+ while (block)
+ {
+ if ((*pCallback)(pvArgs, block->pVirtualAddress, block->dwVirtualSize))
+ {
+ break;
+ }
+
+ block = block->pNext;
+ }
+}
+
+
+#ifdef _DEBUG
+
+void UnlockedLoaderHeap::DumpFreeList()
+{
+ LIMITED_METHOD_CONTRACT;
+ if (m_pFirstFreeBlock == NULL)
+ {
+ printf("FREEDUMP: FreeList is empty\n");
+ }
+ else
+ {
+ LoaderHeapFreeBlock *pBlock = m_pFirstFreeBlock;
+ while (pBlock != NULL)
+ {
+ size_t dwsize = pBlock->m_dwSize;
+ BOOL ccbad = FALSE;
+ BOOL sizeunaligned = FALSE;
+ BOOL sizesmall = FALSE;
+
+ if ( 0 != (dwsize & ALLOC_ALIGN_CONSTANT) )
+ {
+ sizeunaligned = TRUE;
+ }
+ if ( dwsize < sizeof(LoaderHeapBlock))
+ {
+ sizesmall = TRUE;
+ }
+
+ for (size_t i = sizeof(LoaderHeapFreeBlock); i < dwsize; i++)
+ {
+ if ( ((BYTE*)pBlock)[i] != 0xcc )
+ {
+ ccbad = TRUE;
+ break;
+ }
+ }
+
+ printf("Addr = %pxh, Size = %lxh", pBlock, ((ULONG)dwsize));
+ if (ccbad) printf(" *** ERROR: NOT CC'd ***");
+ if (sizeunaligned) printf(" *** ERROR: size not a multiple of ALLOC_ALIGN_CONSTANT ***");
+ if (sizesmall) printf(" *** ERROR: size smaller than sizeof(LoaderHeapFreeBlock) ***");
+ printf("\n");
+
+ pBlock = pBlock->m_pNext;
+ }
+ }
+}
+
+
+void UnlockedLoaderHeap::UnlockedClearEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::ClearEvents(this);
+}
+
+void UnlockedLoaderHeap::UnlockedCompactEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::CompactEvents(this);
+}
+
+void UnlockedLoaderHeap::UnlockedPrintEvents()
+{
+ WRAPPER_NO_CONTRACT;
+ LoaderHeapSniffer::PrintEvents(this);
+}
+
+
+#endif //_DEBUG
+
+//************************************************************************************
+// LOADERHEAP SNIFFER METHODS
+//************************************************************************************
+#ifdef _DEBUG
+
+/*static*/ VOID LoaderHeapSniffer::RecordEvent(UnlockedLoaderHeap *pHeap,
+ AllocationType allocationType,
+ __in const char *szFile,
+ int lineNum,
+ __in const char *szAllocFile,
+ int allocLineNum,
+ void *pMem,
+ size_t dwRequestedSize,
+ size_t dwSize
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT; //If we OOM in here, we just throw the event away.
+ }
+ CONTRACTL_END
+
+ LoaderHeapEvent *pNewEvent;
+ {
+ {
+ FAULT_NOT_FATAL();
+ pNewEvent = new (nothrow) LoaderHeapEvent;
+ }
+ if (!pNewEvent)
+ {
+ if (!(pHeap->m_dwDebugFlags & pHeap->kEncounteredOOM))
+ {
+ pHeap->m_dwDebugFlags |= pHeap->kEncounteredOOM;
+ _ASSERTE(!"LOADERHEAPSNIFFER: Failed allocation of LoaderHeapEvent. Call tracing information will be incomplete.");
+ }
+ }
+ else
+ {
+ pNewEvent->m_allocationType = allocationType;
+ pNewEvent->m_szFile = szFile;
+ pNewEvent->m_lineNum = lineNum;
+ pNewEvent->m_szAllocFile = szAllocFile;
+ pNewEvent->m_allocLineNum = allocLineNum;
+ pNewEvent->m_pMem = pMem;
+ pNewEvent->m_dwRequestedSize = dwRequestedSize;
+ pNewEvent->m_dwSize = dwSize;
+
+ pNewEvent->m_pNext = pHeap->m_pEventList;
+ pHeap->m_pEventList = pNewEvent;
+ }
+ }
+}
+
+
+
+/*static*/
+void LoaderHeapSniffer::ValidateFreeList(UnlockedLoaderHeap *pHeap)
+{
+ CANNOT_HAVE_CONTRACT;
+
+ // No contract. This routine is only called if we've AV'd inside the
+ // loaderheap. The system is already toast. We're trying to be a hero
+ // and produce the best diagnostic info we can. Last thing we need here
+ // is a secondary assert inside the contract stuff.
+ //
+ // This contract violation is permanent.
+ CONTRACT_VIOLATION(ThrowsViolation|FaultViolation|GCViolation|ModeViolation); // This violation won't be removed
+
+ LoaderHeapFreeBlock *pFree = pHeap->m_pFirstFreeBlock;
+ LoaderHeapFreeBlock *pPrev = NULL;
+
+
+ void *pBadAddr = NULL;
+ LoaderHeapFreeBlock *pProbeThis = NULL;
+ const char *pExpected = NULL;
+
+ while (pFree != NULL)
+ {
+ if ( 0 != ( ((ULONG_PTR)pFree) & ALLOC_ALIGN_CONSTANT ))
+ {
+ // Not aligned - can't be a valid freeblock. Most likely we followed a bad pointer from the previous block.
+ pProbeThis = pPrev;
+ pBadAddr = pPrev ? &(pPrev->m_pNext) : &(pHeap->m_pFirstFreeBlock);
+ pExpected = "a pointer to a valid LoaderHeapFreeBlock";
+ break;
+ }
+
+ size_t dwSize = pFree->m_dwSize;
+ if (dwSize < AllocMem_TotalSize(1, pHeap) ||
+ 0 != (dwSize & ALLOC_ALIGN_CONSTANT))
+ {
+ // Size is not a valid value (out of range or unaligned.)
+ pProbeThis = pFree;
+ pBadAddr = &(pFree->m_dwSize);
+ pExpected = "a valid block size (multiple of pointer size)";
+ break;
+ }
+
+ size_t i;
+ for (i = sizeof(LoaderHeapFreeBlock); i < dwSize; i++)
+ {
+ if ( ((BYTE*)pFree)[i] != 0xcc )
+ {
+ pProbeThis = pFree;
+ pBadAddr = i + ((BYTE*)pFree);
+ pExpected = "0xcc (our fill value for free blocks)";
+ break;
+ }
+ }
+ if (i != dwSize)
+ {
+ break;
+ }
+
+
+
+ pPrev = pFree;
+ pFree = pFree->m_pNext;
+ }
+
+ if (pFree == NULL)
+ {
+ return; // No problems found
+ }
+
+ {
+ StackSString message;
+
+ message.Printf("A loaderheap freelist has been corrupted. The bytes at or near address 0x%p appears to have been overwritten. We expected to see %s here.\n"
+ "\n"
+ " LoaderHeap: 0x%p\n"
+ " Suspect address at: 0x%p\n"
+ " Start of suspect freeblock: 0x%p\n"
+ "\n"
+ , pBadAddr
+ , pExpected
+ , pHeap
+ , pBadAddr
+ , pProbeThis
+ );
+
+ if (!(pHeap->m_dwDebugFlags & pHeap->kCallTracing))
+ {
+ message.AppendASCII("\nThe usual reason is that someone wrote past the end of a block or wrote into a block after freeing it."
+ "\nOf course, the culprit is long gone so it's probably too late to debug this now. Try turning on call-tracing"
+ "\nand reproing. We can attempt to find out who last owned the surrounding pieces of memory."
+ "\n"
+ "\nTo turn on call-tracing, set the following registry DWORD value:"
+ "\n"
+ "\n HKLM\\Software\\Microsoft\\.NETFramework\\LoaderHeapCallTracing = 1"
+ "\n"
+ );
+
+ }
+ else
+ {
+ LoaderHeapEvent *pBadAddrEvent = FindEvent(pHeap, pBadAddr);
+
+ message.AppendASCII("*** CALL TRACING ENABLED ***\n\n");
+
+ if (pBadAddrEvent)
+ {
+ message.AppendASCII("\nThe last known owner of the corrupted address was:\n");
+ pBadAddrEvent->Describe(&message);
+ }
+ else
+ {
+ message.AppendASCII("\nNo known owner of last corrupted address.\n");
+ }
+
+ LoaderHeapEvent *pPrevEvent = FindEvent(pHeap, ((BYTE*)pProbeThis) - 1);
+
+ int count = 3;
+ while (count-- &&
+ pPrevEvent != NULL &&
+ ( ((UINT_PTR)pProbeThis) - ((UINT_PTR)(pPrevEvent->m_pMem)) + pPrevEvent->m_dwSize ) < 1024)
+ {
+ message.AppendASCII("\nThis block is located close to the corruption point. ");
+ if (pPrevEvent->QuietValidate())
+ {
+ message.AppendASCII("If it was overrun, it might have caused this.");
+ }
+ else
+ {
+ message.AppendASCII("*** CORRUPTION DETECTED IN THIS BLOCK ***");
+ }
+ pPrevEvent->Describe(&message);
+ pPrevEvent = FindEvent(pHeap, ((BYTE*)(pPrevEvent->m_pMem)) - 1);
+ }
+
+
+ }
+
+ StackScratchBuffer scratch;
+ DbgAssertDialog(__FILE__, __LINE__, (char*) message.GetANSI(scratch));
+
+ }
+
+
+
+}
+
+
+BOOL LoaderHeapEvent::QuietValidate()
+{
+ WRAPPER_NO_CONTRACT;
+
+ if (m_allocationType == kAllocMem)
+ {
+ LoaderHeapValidationTag *pTag = AllocMem_GetTag(m_pMem, m_dwRequestedSize);
+ return (pTag->m_allocationType == m_allocationType && pTag->m_dwRequestedSize == m_dwRequestedSize);
+ }
+ else
+ {
+ // We can't easily validate freed blocks.
+ return TRUE;
+ }
+}
+
+
+#endif //_DEBUG
+
+#ifndef DACCESS_COMPILE
+
+AllocMemTracker::AllocMemTracker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ m_FirstBlock.m_pNext = NULL;
+ m_FirstBlock.m_nextFree = 0;
+ m_pFirstBlock = &m_FirstBlock;
+
+ m_fReleased = FALSE;
+}
+
+AllocMemTracker::~AllocMemTracker()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ if (!m_fReleased)
+ {
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ while (pBlock)
+ {
+ // Do the loop in reverse - loaderheaps work best if
+ // we allocate and backout in LIFO order.
+ for (int i = pBlock->m_nextFree - 1; i >= 0; i--)
+ {
+ AllocMemTrackerNode *pNode = &(pBlock->m_Node[i]);
+ pNode->m_pHeap->RealBackoutMem(pNode->m_pMem
+ ,pNode->m_dwRequestedSize
+#ifdef _DEBUG
+ ,__FILE__
+ ,__LINE__
+ ,pNode->m_szAllocFile
+ ,pNode->m_allocLineNum
+#endif
+ );
+
+ }
+
+ pBlock = pBlock->m_pNext;
+ }
+ }
+
+
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ while (pBlock != &m_FirstBlock)
+ {
+ AllocMemTrackerBlock *pNext = pBlock->m_pNext;
+ delete pBlock;
+ pBlock = pNext;
+ }
+
+ INDEBUG(memset(this, 0xcc, sizeof(*this));)
+}
+
+void *AllocMemTracker::Track(TaggedMemAllocPtr tmap)
+{
+ CONTRACTL
+ {
+ THROWS;
+ INJECT_FAULT(ThrowOutOfMemory(););
+ }
+ CONTRACTL_END
+
+ void *pv = Track_NoThrow(tmap);
+ if (!pv)
+ {
+ ThrowOutOfMemory();
+ }
+ return pv;
+}
+
+void *AllocMemTracker::Track_NoThrow(TaggedMemAllocPtr tmap)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ // Calling Track() after calling SuppressRelease() is almost certainly a bug. You're supposed to call SuppressRelease() only after you're
+ // sure no subsequent failure will force you to backout the memory.
+ _ASSERTE( (!m_fReleased) && "You've already called SuppressRelease on this AllocMemTracker which implies you've passed your point of no failure. Why are you still doing allocations?");
+
+
+ if (tmap.m_pMem != NULL)
+ {
+ AllocMemHolder<void*> holder(tmap); // If anything goes wrong in here, this holder will backout the allocation for the caller.
+ if (m_fReleased)
+ {
+ holder.SuppressRelease();
+ }
+ AllocMemTrackerBlock *pBlock = m_pFirstBlock;
+ if (pBlock->m_nextFree == kAllocMemTrackerBlockSize)
+ {
+ AllocMemTrackerBlock *pNewBlock = new (nothrow) AllocMemTrackerBlock;
+ if (!pNewBlock)
+ {
+ return NULL;
+ }
+
+ pNewBlock->m_pNext = m_pFirstBlock;
+ pNewBlock->m_nextFree = 0;
+
+ m_pFirstBlock = pNewBlock;
+
+ pBlock = pNewBlock;
+ }
+
+ // From here on, we can't fail
+ pBlock->m_Node[pBlock->m_nextFree].m_pHeap = tmap.m_pHeap;
+ pBlock->m_Node[pBlock->m_nextFree].m_pMem = tmap.m_pMem;
+ pBlock->m_Node[pBlock->m_nextFree].m_dwRequestedSize = tmap.m_dwRequestedSize;
+#ifdef _DEBUG
+ pBlock->m_Node[pBlock->m_nextFree].m_szAllocFile = tmap.m_szFile;
+ pBlock->m_Node[pBlock->m_nextFree].m_allocLineNum = tmap.m_lineNum;
+#endif
+
+ pBlock->m_nextFree++;
+
+ holder.SuppressRelease();
+
+
+ }
+ return (void *)tmap;
+}
+
+
+void AllocMemTracker::SuppressRelease()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_fReleased = TRUE;
+}
+
+#endif //#ifndef DACCESS_COMPILE
diff --git a/src/coreclr/utilcode/log.cpp b/src/coreclr/utilcode/log.cpp
new file mode 100644
index 00000000000..37edc05b5cd
--- /dev/null
+++ b/src/coreclr/utilcode/log.cpp
@@ -0,0 +1,420 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// Simple Logging Facility
+//
+#include "stdafx.h"
+
+//
+// Define LOGGING by default in a checked build. If you want to log in a free
+// build, define logging independent of _DEBUG here and each place you want
+// to use it.
+//
+#ifdef _DEBUG
+#define LOGGING
+#endif
+
+#include "log.h"
+#include "utilcode.h"
+
+#ifdef LOGGING
+
+#define DEFAULT_LOGFILE_NAME W("COMPLUS.LOG")
+
+#define LOG_ENABLE_FILE_LOGGING 0x0001
+#define LOG_ENABLE_FLUSH_FILE 0x0002
+#define LOG_ENABLE_CONSOLE_LOGGING 0x0004
+#define LOG_ENABLE_APPEND_FILE 0x0010
+#define LOG_ENABLE_DEBUGGER_LOGGING 0x0020
+#define LOG_ENABLE 0x0040
+
+
+static DWORD LogFlags = 0;
+static CQuickWSTR szLogFileName;
+static HANDLE LogFileHandle = INVALID_HANDLE_VALUE;
+static volatile HANDLE LogFileMutex = 0;
+static DWORD LogFacilityMask = LF_ALL;
+static DWORD LogFacilityMask2 = 0;
+static DWORD LogVMLevel = LL_INFO100;
+ // <TODO>@todo FIX should probably only display warnings and above by default</TODO>
+
+
+VOID InitLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ // <TODO>FIX bit of a workaround for now, check for the log file in the
+ // registry and if there, turn on file logging VPM</TODO>
+
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogEnable, LOG_ENABLE);
+ LogFacilityMask = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility, LogFacilityMask) | LF_ALWAYS;
+ LogVMLevel = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::EXTERNAL_LogLevel, LogVMLevel);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFileAppend, LOG_ENABLE_APPEND_FILE);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogFlushFile, LOG_ENABLE_FLUSH_FILE);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToDebugger, LOG_ENABLE_DEBUGGER_LOGGING);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToFile, LOG_ENABLE_FILE_LOGGING);
+ LogFlags |= REGUTIL::GetConfigFlag_DontUse_(CLRConfig::INTERNAL_LogToConsole, LOG_ENABLE_CONSOLE_LOGGING);
+
+ LogFacilityMask2 = REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogFacility2, LogFacilityMask2) | LF_ALWAYS;
+
+ if (SUCCEEDED(szLogFileName.ReSizeNoThrow(MAX_LONGPATH)))
+ {
+ wcscpy_s(szLogFileName.Ptr(), szLogFileName.Size(), DEFAULT_LOGFILE_NAME);
+ }
+
+ LPWSTR fileName = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_LogFile);
+ if (fileName != 0)
+ {
+ if (SUCCEEDED(szLogFileName.ReSizeNoThrow(wcslen(fileName) + 32)))
+ {
+ wcscpy_s(szLogFileName.Ptr(), szLogFileName.Size(), fileName);
+ }
+ delete fileName;
+ }
+
+ if (REGUTIL::GetConfigDWORD_DontUse_(CLRConfig::INTERNAL_LogWithPid, FALSE))
+ {
+ WCHAR szPid[20];
+ swprintf_s(szPid, COUNTOF(szPid), W(".%d"), GetCurrentProcessId());
+ wcscat_s(szLogFileName.Ptr(), szLogFileName.Size(), szPid);
+ }
+
+ if ((LogFlags & LOG_ENABLE) &&
+ (LogFlags & LOG_ENABLE_FILE_LOGGING) &&
+ (szLogFileName.Size() > 0) &&
+ (LogFileHandle == INVALID_HANDLE_VALUE))
+ {
+ DWORD fdwCreate = (LogFlags & LOG_ENABLE_APPEND_FILE) ? OPEN_ALWAYS : CREATE_ALWAYS;
+ LogFileHandle = WszCreateFile(
+ szLogFileName.Ptr(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ fdwCreate,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0),
+ NULL);
+
+ // Some other logging may be going on, try again with another file name
+ if (LogFileHandle == INVALID_HANDLE_VALUE && wcslen(szLogFileName.Ptr()) + 3 <= szLogFileName.Size())
+ {
+ WCHAR* ptr = szLogFileName.Ptr() + wcslen(szLogFileName.Ptr()) + 1;
+ ptr[-1] = W('.');
+ ptr[0] = W('0');
+ ptr[1] = 0;
+
+ for(int i = 0; i < 10; i++)
+ {
+ LogFileHandle = WszCreateFile(
+ szLogFileName.Ptr(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ fdwCreate,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | ((LogFlags & LOG_ENABLE_FLUSH_FILE) ? FILE_FLAG_WRITE_THROUGH : 0),
+ NULL);
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ break;
+ *ptr = *ptr + 1;
+ }
+ if (LogFileHandle == INVALID_HANDLE_VALUE) {
+ int ret = WszWideCharToMultiByte(CP_ACP, 0, szLogFileName.Ptr(), -1, NULL, 0, NULL, NULL);
+ const char *msg = "Could not open log file, logging to ";
+ DWORD msgLen = (DWORD)strlen(msg);
+ CQuickSTR buff;
+ if (SUCCEEDED(buff.ReSizeNoThrow(ret + msgLen)))
+ {
+ strcpy_s(buff.Ptr(), buff.Size(), msg);
+ WszWideCharToMultiByte(CP_ACP, 0, szLogFileName.Ptr(), -1, buff.Ptr() + msgLen, ret, NULL, NULL);
+ msg = buff.Ptr();
+ }
+ else
+ {
+ msg = "Could not open log file";
+ }
+ DWORD written;
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msg, (DWORD)strlen(msg), &written, 0);
+ }
+ }
+ if (LogFileHandle == INVALID_HANDLE_VALUE)
+ UtilMessageBoxNonLocalized(NULL, W("Could not open log file"), W("CLR logging"), MB_OK | MB_ICONINFORMATION, FALSE, TRUE);
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ if (LogFlags & LOG_ENABLE_APPEND_FILE)
+ SetFilePointer(LogFileHandle, 0, NULL, FILE_END);
+ LogSpew( LF_ALWAYS, FATALERROR, "************************ New Output *****************\n" );
+ }
+ }
+}
+
+VOID EnterLogLock()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // We don't care about violating CANNOT_TAKE_LOCK in debug-only builds, and it's
+ // rather hard to care about this, as we LOG all over the place.
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ if(LogFileMutex != 0)
+ {
+ DWORD status;
+ status = WaitForSingleObjectEx(LogFileMutex, INFINITE, FALSE);
+ _ASSERTE(WAIT_OBJECT_0 == status);
+ }
+}
+
+VOID LeaveLogLock()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if(LogFileMutex != 0)
+ {
+ BOOL success;
+ success = ReleaseMutex(LogFileMutex);
+ _ASSERTE(success);
+ }
+}
+
+static volatile bool bLoggingInitialized = false;
+VOID InitializeLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ if (bLoggingInitialized)
+ return;
+
+ HANDLE mutex = WszCreateMutex(NULL, FALSE, NULL);
+ _ASSERTE(mutex != 0);
+ if (InterlockedCompareExchangeT(&LogFileMutex, mutex, 0) != 0)
+ {
+ CloseHandle(mutex);
+ }
+
+ EnterLogLock();
+ if (!bLoggingInitialized)
+ {
+ InitLogging(); // You can call this in the debugger to fetch new settings
+ bLoggingInitialized = true;
+ }
+ LeaveLogLock();
+}
+
+VOID FlushLogging() {
+ STATIC_CONTRACT_NOTHROW;
+
+ if (LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ // We must take the lock, as an OS deadlock can occur between
+ // FlushFileBuffers and WriteFile.
+ EnterLogLock();
+ FlushFileBuffers( LogFileHandle );
+ LeaveLogLock();
+ }
+}
+
+VOID ShutdownLogging()
+{
+ STATIC_CONTRACT_NOTHROW;
+
+ if (LogFileHandle != INVALID_HANDLE_VALUE) {
+ LogSpew( LF_ALWAYS, FATALERROR, "Logging shutting down\n");
+ CloseHandle( LogFileHandle );
+ }
+ LogFileHandle = INVALID_HANDLE_VALUE;
+ bLoggingInitialized = false;
+}
+
+
+bool LoggingEnabled()
+{
+ STATIC_CONTRACT_LEAF;
+
+ return ((LogFlags & LOG_ENABLE) != 0);
+}
+
+
+bool LoggingOn(DWORD facility, DWORD level) {
+ STATIC_CONTRACT_LEAF;
+
+ _ASSERTE(LogFacilityMask & LF_ALWAYS); // LF_ALWAYS should always be enabled
+
+ return((LogFlags & LOG_ENABLE) &&
+ level <= LogVMLevel &&
+ (facility & LogFacilityMask));
+}
+
+bool Logging2On(DWORD facility2, DWORD level) {
+ STATIC_CONTRACT_LEAF;
+
+ _ASSERTE(LogFacilityMask2 & LF_ALWAYS); // LF_ALWAYS should always be enabled
+
+ return((LogFlags & LOG_ENABLE) &&
+ level <= LogVMLevel &&
+ (facility2 & LogFacilityMask2));
+}
+
+//
+// Don't use me directly, use the macros in log.h
+//
+VOID LogSpewValist(DWORD facility, DWORD level, const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!LoggingOn(facility, level))
+ return;
+
+ DEBUG_ONLY_FUNCTION;
+
+ LogSpewAlwaysValist(fmt, args);
+}
+
+
+VOID LogSpew2Valist(DWORD facility2, DWORD level, const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (!Logging2On(facility2, level))
+ return;
+
+ DEBUG_ONLY_FUNCTION;
+
+ LogSpewAlwaysValist(fmt, args);
+}
+
+
+VOID LogSpewAlwaysValist(const char *fmt, va_list args)
+{
+ SCAN_IGNORE_FAULT; // calls to new (nothrow) in logging code are OK
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ DEBUG_ONLY_FUNCTION;
+
+ // We can't do heap allocations at all. The current thread may have
+ // suspended another thread, and the suspended thread may be inside of the
+ // heap lock.
+ //
+ // (Some historical comments:)
+ //
+ // We must operate with a very small stack (in case we're logging durring
+ // a stack overflow)
+ //
+ // We're going to bypass our debug memory allocator and just allocate memory from
+ // the process heap. Why? Because our debug memory allocator will log out of memory
+ // conditions. If we're low on memory, and we try to log an out of memory condition, and we try
+ // and allocate memory again using the debug allocator, we could (and probably will) hit
+ // another low memory condition, try to log it, and we spin indefinately until we hit a stack overflow.
+
+ const int BUFFERSIZE = 1000;
+ static char rgchBuffer[BUFFERSIZE];
+
+ EnterLogLock();
+
+ char * pBuffer = &rgchBuffer[0];
+ DWORD buflen = 0;
+ DWORD written;
+
+ static bool needsPrefix = true;
+
+ if (needsPrefix)
+ buflen = sprintf_s(pBuffer, COUNTOF(rgchBuffer), "TID %04x: ", GetCurrentThreadId());
+
+ needsPrefix = (fmt[strlen(fmt)-1] == '\n');
+
+ int cCountWritten = _vsnprintf_s(&pBuffer[buflen], BUFFERSIZE-buflen, _TRUNCATE, fmt, args );
+ pBuffer[BUFFERSIZE-1] = 0;
+ if (cCountWritten < 0) {
+ buflen = BUFFERSIZE - 1;
+ } else {
+ buflen += cCountWritten;
+ }
+
+ // Its a little late for this, but at least you wont continue
+ // trashing your program...
+ _ASSERTE((buflen < (DWORD) BUFFERSIZE) && "Log text is too long!") ;
+
+#if !TARGET_UNIX
+ //convert NL's to CR NL to fixup notepad
+ const int BUFFERSIZE2 = BUFFERSIZE + 500;
+ char rgchBuffer2[BUFFERSIZE2];
+ char * pBuffer2 = &rgchBuffer2[0];
+
+ char *d = pBuffer2;
+ for (char *p = pBuffer; *p != '\0'; p++)
+ {
+ if (*p == '\n') {
+ _ASSERTE(d < pBuffer2 + BUFFERSIZE2);
+ *(d++) = '\r';
+ }
+
+ _ASSERTE(d < pBuffer2 + BUFFERSIZE2);
+ *(d++) = *p;
+ }
+ *d = 0;
+
+ buflen = (DWORD)(d - pBuffer2);
+ pBuffer = pBuffer2;
+#endif // TARGET_UNIX
+
+ if (LogFlags & LOG_ENABLE_FILE_LOGGING && LogFileHandle != INVALID_HANDLE_VALUE)
+ {
+ WriteFile(LogFileHandle, pBuffer, buflen, &written, NULL);
+ if (LogFlags & LOG_ENABLE_FLUSH_FILE) {
+ FlushFileBuffers( LogFileHandle );
+ }
+ }
+
+ if (LogFlags & LOG_ENABLE_CONSOLE_LOGGING)
+ {
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), pBuffer, buflen, &written, 0);
+ //<TODO>@TODO ...Unnecessary to flush console?</TODO>
+ if (LogFlags & LOG_ENABLE_FLUSH_FILE)
+ FlushFileBuffers( GetStdHandle(STD_OUTPUT_HANDLE) );
+ }
+
+ if (LogFlags & LOG_ENABLE_DEBUGGER_LOGGING)
+ {
+ OutputDebugStringA(pBuffer);
+ }
+
+ LeaveLogLock();
+}
+
+VOID LogSpew(DWORD facility, DWORD level, const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ va_list args;
+ va_start(args, fmt);
+ LogSpewValist (facility, level, fmt, args);
+ va_end(args);
+}
+
+VOID LogSpew2(DWORD facility2, DWORD level, const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ va_list args;
+ va_start(args, fmt);
+ LogSpew2Valist(facility2, level, fmt, args);
+ va_end(args);
+}
+
+VOID LogSpewAlways (const char *fmt, ... )
+{
+ STATIC_CONTRACT_WRAPPER;
+
+ va_list args;
+ va_start(args, fmt);
+ LogSpewValist (LF_ALWAYS, LL_ALWAYS, fmt, args);
+ va_end(args);
+}
+
+#endif // LOGGING
+
diff --git a/src/coreclr/utilcode/longfilepathwrappers.cpp b/src/coreclr/utilcode/longfilepathwrappers.cpp
new file mode 100644
index 00000000000..3afc611c3b2
--- /dev/null
+++ b/src/coreclr/utilcode/longfilepathwrappers.cpp
@@ -0,0 +1,959 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+#include "windows.h"
+#include "longfilepathwrappers.h"
+#include "sstring.h"
+#include "ex.h"
+
+class LongFile
+{
+private:
+#ifdef HOST_WINDOWS
+ static const WCHAR* ExtendedPrefix;
+ static const WCHAR* DevicePathPrefix;
+ static const WCHAR* UNCPathPrefix;
+ static const WCHAR* UNCExtendedPathPrefix;
+ static const WCHAR VolumeSeparatorChar;
+ #define UNCPATHPREFIX W("\\\\")
+#endif //HOST_WINDOWS
+ static const WCHAR DirectorySeparatorChar;
+ static const WCHAR AltDirectorySeparatorChar;
+public:
+ static BOOL IsExtended(SString & path);
+ static BOOL IsUNCExtended(SString & path);
+ static BOOL ContainsDirectorySeparator(SString & path);
+ static BOOL IsDirectorySeparator(WCHAR c);
+ static BOOL IsPathNotFullyQualified(SString & path);
+ static BOOL IsDevice(SString & path);
+
+ static HRESULT NormalizePath(SString& path);
+
+#ifdef HOST_WINDOWS
+ static void NormalizeDirectorySeparators(SString& path);
+#endif
+};
+
+HMODULE
+LoadLibraryExWrapper(
+ LPCWSTR lpLibFileName,
+ HANDLE hFile,
+ DWORD dwFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HMODULE ret = NULL;
+ DWORD lastError;
+
+ EX_TRY
+ {
+
+ LongPathString path(LongPathString::Literal, lpLibFileName);
+
+ if (LongFile::IsPathNotFullyQualified(path) || SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+#ifdef HOST_WINDOWS
+ //Adding the assert to ensure relative paths which are not just filenames are not used for LoadLibrary Calls
+ _ASSERTE(!LongFile::IsPathNotFullyQualified(path) || !LongFile::ContainsDirectorySeparator(path));
+ LongFile::NormalizeDirectorySeparators(path);
+#endif //HOST_WINDOWS
+
+ ret = LoadLibraryExW(path.GetUnicode(), hFile, dwFlags);
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if(ret == NULL)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+HANDLE
+CreateFileWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ DWORD dwDesiredAccess,
+ _In_ DWORD dwShareMode,
+ _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ _In_ DWORD dwCreationDisposition,
+ _In_ DWORD dwFlagsAndAttributes,
+ _In_opt_ HANDLE hTemplateFile
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD lastError;
+ HANDLE ret = INVALID_HANDLE_VALUE;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = CreateFileW(path.GetUnicode(),
+ dwDesiredAccess,
+ dwShareMode,
+ lpSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ hTemplateFile);
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_HANDLE_VALUE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD
+GetFileAttributesWrapper(
+ _In_ LPCWSTR lpFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = INVALID_FILE_ATTRIBUTES;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = GetFileAttributesW(
+ path.GetUnicode()
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_FILE_ATTRIBUTES)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+GetFileAttributesExWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ GET_FILEEX_INFO_LEVELS fInfoLevelId,
+ _Out_writes_bytes_(sizeof(WIN32_FILE_ATTRIBUTE_DATA)) LPVOID lpFileInformation
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = GetFileAttributesExW(
+ path.GetUnicode(),
+ fInfoLevelId,
+ lpFileInformation
+ );
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+DeleteFileWrapper(
+ _In_ LPCWSTR lpFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = DeleteFileW(
+ path.GetUnicode()
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+BOOL
+MoveFileExWrapper(
+ _In_ LPCWSTR lpExistingFileName,
+ _In_opt_ LPCWSTR lpNewFileName,
+ _In_ DWORD dwFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
+ LongPathString Newpath(LongPathString::Literal, lpNewFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
+ {
+ ret = MoveFileExW(
+ Existingpath.GetUnicode(),
+ Newpath.GetUnicode(),
+ dwFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+
+}
+
+DWORD
+SearchPathWrapper(
+ _In_opt_ LPCWSTR lpPath,
+ _In_ LPCWSTR lpFileName,
+ _In_opt_ LPCWSTR lpExtension,
+ _In_ BOOL getPath,
+ SString& lpBuffer,
+ _Out_opt_ LPWSTR * lpFilePart
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpPath);
+
+ if (lpPath != NULL)
+ {
+ if (FAILED(LongFile::NormalizePath(Existingpath)))
+ {
+ ret = FALSE;
+ }
+ else
+ {
+ lpPath = Existingpath.GetUnicode();
+ }
+ }
+
+ if (!getPath)
+ {
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ 0,
+ NULL,
+ NULL
+ );
+ }
+ else
+ {
+ COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
+
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ lpFilePart
+ );
+
+ if (ret > size)
+ {
+ lpBuffer.CloseBuffer();
+ ret = SearchPathW(
+ lpPath,
+ lpFileName,
+ lpExtension,
+ ret,
+ lpBuffer.OpenUnicodeBuffer(ret - 1),
+ lpFilePart
+ );
+ }
+
+ lpBuffer.CloseBuffer(ret);
+
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+
+}
+
+DWORD
+GetModuleFileNameWrapper(
+ _In_opt_ HMODULE hModule,
+ SString& buffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ COUNT_T size = buffer.GetUnicodeAllocation() + 1;
+
+ ret = GetModuleFileNameW(
+ hModule,
+ buffer.OpenUnicodeBuffer(size - 1),
+ (DWORD)size
+ );
+
+
+ while (ret == size )
+ {
+ buffer.CloseBuffer();
+ size = size * 2;
+ ret = GetModuleFileNameW(
+ hModule,
+ buffer.OpenUnicodeBuffer(size - 1),
+ (DWORD)size
+ );
+
+ }
+
+
+ lastError = GetLastError();
+ buffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+UINT WINAPI GetTempFileNameWrapper(
+ _In_ LPCTSTR lpPathName,
+ _In_ LPCTSTR lpPrefixString,
+ _In_ UINT uUnique,
+ SString& lpTempFileName
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ UINT ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+ WCHAR* buffer = lpTempFileName.OpenUnicodeBuffer(size - 1);
+ ret = GetTempFileNameW(
+ lpPathName,
+ lpPrefixString,
+ uUnique,
+ buffer
+ );
+
+ lastError = GetLastError();
+ size = (COUNT_T)wcslen(buffer);
+ lpTempFileName.CloseBuffer(size);
+
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+DWORD WINAPI GetTempPathWrapper(
+ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+
+ ret = GetTempPathW(
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1)
+ );
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD WINAPI GetCurrentDirectoryWrapper(
+ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ //Change the behaviour in Redstone to retry
+ COUNT_T size = MAX_LONGPATH;
+
+ ret = GetCurrentDirectoryW(
+ size,
+ lpBuffer.OpenUnicodeBuffer(size - 1)
+ );
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+DWORD WINAPI GetEnvironmentVariableWrapper(
+ _In_opt_ LPCTSTR lpName,
+ _Out_opt_ SString& lpBuffer
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ DWORD lastError;
+
+ EX_TRY
+ {
+
+ COUNT_T size = lpBuffer.GetUnicodeAllocation() + 1;
+
+ ret = GetEnvironmentVariableW(
+ lpName,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ size
+ );
+
+ // We loop round getting the length of the env var and then trying to copy
+ // the value into a the allocated buffer. Usually we'll go through this loop
+ // precisely once, but the caution is ncessary in case the variable mutates
+ // beneath us, as the environment variable can be modified by another thread
+ //between two calls to GetEnvironmentVariableW
+
+ while (ret > size)
+ {
+ size = ret;
+ lpBuffer.CloseBuffer();
+ ret = GetEnvironmentVariableW(
+ lpName,
+ lpBuffer.OpenUnicodeBuffer(size - 1),
+ size);
+ }
+
+ lastError = GetLastError();
+ lpBuffer.CloseBuffer(ret);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+ else if (ret == 0)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+
+#ifdef HOST_WINDOWS
+
+BOOL
+CopyFileExWrapper(
+ _In_ LPCWSTR lpExistingFileName,
+ _In_ LPCWSTR lpNewFileName,
+ _In_opt_ LPPROGRESS_ROUTINE lpProgressRoutine,
+ _In_opt_ LPVOID lpData,
+ _When_(pbCancel != NULL, _Pre_satisfies_(*pbCancel == FALSE))
+ _Inout_opt_ LPBOOL pbCancel,
+ _In_ DWORD dwCopyFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ BOOL ret = FALSE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString Existingpath(LongPathString::Literal, lpExistingFileName);
+ LongPathString Newpath(LongPathString::Literal, lpNewFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(Existingpath)) && SUCCEEDED(LongFile::NormalizePath(Newpath)))
+ {
+ ret = CopyFileExW(
+ Existingpath.GetUnicode(),
+ Newpath.GetUnicode(),
+ lpProgressRoutine,
+ lpData,
+ pbCancel,
+ dwCopyFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == FALSE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+
+HANDLE
+FindFirstFileExWrapper(
+ _In_ LPCWSTR lpFileName,
+ _In_ FINDEX_INFO_LEVELS fInfoLevelId,
+ _Out_writes_bytes_(sizeof(WIN32_FIND_DATAW)) LPVOID lpFindFileData,
+ _In_ FINDEX_SEARCH_OPS fSearchOp,
+ _Reserved_ LPVOID lpSearchFilter,
+ _In_ DWORD dwAdditionalFlags
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ HANDLE ret = INVALID_HANDLE_VALUE;
+ DWORD lastError;
+
+ EX_TRY
+ {
+ LongPathString path(LongPathString::Literal, lpFileName);
+
+ if (SUCCEEDED(LongFile::NormalizePath(path)))
+ {
+ ret = FindFirstFileExW(
+ path.GetUnicode(),
+ fInfoLevelId,
+ lpFindFileData,
+ fSearchOp,
+ lpSearchFilter,
+ dwAdditionalFlags
+ );
+ }
+
+ lastError = GetLastError();
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK )
+ {
+ SetLastError(hr);
+ }
+ else if(ret == INVALID_HANDLE_VALUE)
+ {
+ SetLastError(lastError);
+ }
+
+ return ret;
+}
+#endif // HOST_WINDOWS
+
+//Implementation of LongFile Helpers
+const WCHAR LongFile::DirectorySeparatorChar = W('\\');
+const WCHAR LongFile::AltDirectorySeparatorChar = W('/');
+#ifdef HOST_WINDOWS
+const WCHAR LongFile::VolumeSeparatorChar = W(':');
+const WCHAR* LongFile::ExtendedPrefix = W("\\\\?\\");
+const WCHAR* LongFile::DevicePathPrefix = W("\\\\.\\");
+const WCHAR* LongFile::UNCExtendedPathPrefix = W("\\\\?\\UNC\\");
+const WCHAR* LongFile::UNCPathPrefix = UNCPATHPREFIX;
+
+void LongFile::NormalizeDirectorySeparators(SString& path)
+{
+ for(SString::Iterator i = path.Begin(); i < path.End(); ++i)
+ {
+ if (*i == AltDirectorySeparatorChar)
+ {
+ path.Replace(i, DirectorySeparatorChar);
+ }
+ }
+}
+
+BOOL LongFile::IsExtended(SString & path)
+{
+ return path.BeginsWith(ExtendedPrefix);
+}
+
+BOOL LongFile::IsUNCExtended(SString & path)
+{
+
+ return path.BeginsWith(UNCExtendedPathPrefix);
+}
+
+// Relative here means it could be relative to current directory on the relevant drive
+// NOTE: Relative segments ( \..\) are not considered relative
+// Returns true if the path specified is relative to the current drive or working directory.
+// Returns false if the path is fixed to a specific drive or UNC path. This method does no
+// validation of the path (URIs will be returned as relative as a result).
+// Handles paths that use the alternate directory separator. It is a frequent mistake to
+// assume that rooted paths (Path.IsPathRooted) are not relative. This isn't the case.
+
+BOOL LongFile::IsPathNotFullyQualified(SString & path)
+{
+ if (path.GetCount() < 2)
+ {
+ return TRUE; // It isn't fixed, it must be relative. There is no way to specify a fixed path with one character (or less).
+ }
+
+ if (IsDirectorySeparator(path[0]))
+ {
+ return !IsDirectorySeparator(path[1]); // There is no valid way to specify a relative path with two initial slashes
+ }
+
+ return !((path.GetCount() >= 3) //The only way to specify a fixed path that doesn't begin with two slashes is the drive, colon, slash format- i.e. "C:\"
+ && (path[1] == VolumeSeparatorChar)
+ && IsDirectorySeparator(path[2]));
+}
+
+BOOL LongFile::IsDevice(SString & path)
+{
+ return path.BeginsWith(DevicePathPrefix);
+}
+
+// This function will normalize paths if the path length exceeds MAX_PATH
+// The normalization examples are :
+// C:\foo\<long>\bar => \\?\C:\foo\<long>\bar
+// \\server\<long>\bar => \\?\UNC\server\<long>\bar
+HRESULT LongFile::NormalizePath(SString & path)
+{
+ HRESULT hr = S_OK;
+ DWORD ret = 0;
+ COUNT_T prefixLen = 0;
+ if (path.IsEmpty()|| IsDevice(path) || IsExtended(path) || IsUNCExtended(path))
+ return S_OK;
+
+ if (!IsPathNotFullyQualified(path) && path.GetCount() < MAX_LONGPATH)
+ return S_OK;
+
+ //Now the path will be normalized
+
+ SString originalPath(path);
+ SString prefix(ExtendedPrefix);
+ prefixLen = prefix.GetCount();
+
+ if (path.BeginsWith(UNCPathPrefix))
+ {
+ prefix.Set(UNCExtendedPathPrefix);
+ //In this case if path is \\server the extended syntax should be like \\?\UNC\server
+ //The below logic populates the path from prefixLen offset from the start. This ensures that first 2 characters are overwritten
+ //
+ prefixLen = prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX);
+ _ASSERTE(prefixLen > 0 );
+ }
+
+
+ COUNT_T size = path.GetUnicodeAllocation() + 1;
+ WCHAR* buffer = path.OpenUnicodeBuffer(size - 1);
+
+ ret = GetFullPathNameW(
+ originalPath.GetUnicode(),
+ size - prefixLen, //memory avilable for path after reserving for prefix
+ (buffer + prefixLen), //reserve memory for prefix
+ NULL
+ );
+
+ if (ret == 0)
+ {
+ return E_FAIL;
+ }
+
+ if (ret > size - prefixLen)
+ {
+ path.CloseBuffer();
+ size = ret + prefixLen;
+ buffer = path.OpenUnicodeBuffer(size -1);
+
+ ret = GetFullPathNameW(
+ originalPath.GetUnicode(),
+ ret, // memory required for the path
+ (buffer + prefixLen), //reserve memory for prefix
+ NULL
+ );
+
+ _ASSERTE(ret < size - prefixLen);
+
+ if (ret == 0)
+ {
+ return E_FAIL;
+ }
+ }
+
+ SString fullpath(SString::Literal,buffer + prefixLen);
+
+ //Check if the resolved path is a UNC. By default we assume relative path to resolve to disk
+ if (fullpath.BeginsWith(UNCPathPrefix) && prefixLen != prefix.GetCount() - (COUNT_T)wcslen(UNCPATHPREFIX))
+ {
+
+ //Remove the leading '\\' from the UNC path to be replaced with UNCExtendedPathPrefix
+ fullpath.Replace(fullpath.Begin(), (COUNT_T)wcslen(UNCPATHPREFIX), UNCExtendedPathPrefix);
+ path.CloseBuffer();
+ path.Set(fullpath);
+ }
+ else
+ {
+ //wcscpy_s always termintes with NULL, so we are saving the character that will be overwriiten
+ WCHAR temp = buffer[prefix.GetCount()];
+ wcscpy_s(buffer, prefix.GetCount() + 1, prefix.GetUnicode());
+ buffer[prefix.GetCount()] = temp;
+ path.CloseBuffer(ret + prefixLen);
+ }
+
+ return S_OK;
+}
+#else
+BOOL LongFile::IsExtended(SString & path)
+{
+ return FALSE;
+}
+
+BOOL LongFile::IsUNCExtended(SString & path)
+{
+ return FALSE;
+}
+
+BOOL LongFile::IsPathNotFullyQualified(SString & path)
+{
+ return TRUE;
+}
+
+BOOL LongFile::IsDevice(SString & path)
+{
+ return FALSE;
+}
+
+//Don't need to do anything For XPlat
+HRESULT LongFile::NormalizePath(SString & path)
+{
+ return S_OK;
+}
+#endif //HOST_WINDOWS
+
+BOOL LongFile::ContainsDirectorySeparator(SString & path)
+{
+ return path.Find(path.Begin(), DirectorySeparatorChar) || path.Find(path.Begin(), AltDirectorySeparatorChar);
+}
+
+BOOL LongFile::IsDirectorySeparator(WCHAR c)
+{
+ return c == DirectorySeparatorChar || c == AltDirectorySeparatorChar;
+}
+
+
+
diff --git a/src/coreclr/utilcode/makepath.cpp b/src/coreclr/utilcode/makepath.cpp
new file mode 100644
index 00000000000..4bf8787242a
--- /dev/null
+++ b/src/coreclr/utilcode/makepath.cpp
@@ -0,0 +1,212 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+/***
+*makepath.c - create path name from components
+*
+
+*
+*Purpose:
+* To provide support for creation of full path names from components
+*
+*******************************************************************************/
+#include "stdafx.h"
+#include "winwrap.h"
+#include "utilcode.h"
+#include "ex.h"
+
+
+/***
+*void Makepath() - build path name from components
+*
+*Purpose:
+* create a path name from its individual components
+*
+*Entry:
+* CQuickWSTR &szPath - Buffer for constructed path
+* WCHAR *drive - pointer to drive component, may or may not contain
+* trailing ':'
+* WCHAR *dir - pointer to subdirectory component, may or may not include
+* leading and/or trailing '/' or '\' characters
+* WCHAR *fname - pointer to file base name component
+* WCHAR *ext - pointer to extension component, may or may not contain
+* a leading '.'.
+*
+*Exit:
+* path - pointer to constructed path name
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void MakePath (
+ __out CQuickWSTR &szPath,
+ __in LPCWSTR drive,
+ __in LPCWSTR dir,
+ __in LPCWSTR fname,
+ __in LPCWSTR ext
+ )
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END
+
+ SIZE_T maxCount = 4 // Possible separators between components, plus null terminator
+ + (drive != nullptr ? 2 : 0)
+ + (dir != nullptr ? wcslen(dir) : 0)
+ + (fname != nullptr ? wcslen(fname) : 0)
+ + (ext != nullptr ? wcslen(ext) : 0);
+ LPWSTR path = szPath.AllocNoThrow(maxCount);
+
+ const WCHAR *p;
+ DWORD count = 0;
+
+ /* we assume that the arguments are in the following form (although we
+ * do not diagnose invalid arguments or illegal filenames (such as
+ * names longer than 8.3 or with illegal characters in them)
+ *
+ * drive:
+ * A ; or
+ * A:
+ * dir:
+ * \top\next\last\ ; or
+ * /top/next/last/ ; or
+ * either of the above forms with either/both the leading
+ * and trailing / or \ removed. Mixed use of '/' and '\' is
+ * also tolerated
+ * fname:
+ * any valid file name
+ * ext:
+ * any valid extension (none if empty or null )
+ */
+
+ /* copy drive */
+
+ if (drive && *drive) {
+ *path++ = *drive;
+ *path++ = _T(':');
+ count += 2;
+ }
+
+ /* copy dir */
+
+ if ((p = dir)) {
+ while (*p) {
+ *path++ = *p++;
+ count++;
+
+ _ASSERTE(count < maxCount);
+ }
+
+ // suppress warning for the following line; this is safe but would require significant code
+ // delta for prefast to understand.
+#ifdef _PREFAST_
+ #pragma warning( suppress: 26001 )
+#endif
+ if (*(p-1) != _T('/') && *(p-1) != _T('\\')) {
+ *path++ = _T('\\');
+ count++;
+
+ _ASSERTE(count < maxCount);
+ }
+ }
+
+ /* copy fname */
+
+ if ((p = fname)) {
+ while (*p) {
+ *path++ = *p++;
+ count++;
+
+ _ASSERTE(count < maxCount);
+ }
+ }
+
+ /* copy ext, including 0-terminator - check to see if a '.' needs
+ * to be inserted.
+ */
+
+ if ((p = ext)) {
+ if (*p && *p != _T('.')) {
+ *path++ = _T('.');
+ count++;
+
+ _ASSERTE(count < maxCount);
+ }
+
+ while ((*path++ = *p++)) {
+ count++;
+
+ _ASSERTE(count < maxCount);
+ }
+ }
+ else {
+ /* better add the 0-terminator */
+ *path = _T('\0');
+ }
+
+ szPath.Shrink(count + 1);
+}
+
+
+// Returns the directory for clr module. So, if path was for "C:\Dir1\Dir2\Filename.DLL",
+// then this would return "C:\Dir1\Dir2\" (note the trailing backslash).HRESULT GetClrModuleDirectory(SString& wszPath)
+HRESULT GetClrModuleDirectory(SString& wszPath)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ DWORD dwRet = GetClrModulePathName(wszPath);
+
+ if (dwRet == 0)
+ { // Some other error.
+ return HRESULT_FROM_GetLastError();
+ }
+
+ CopySystemDirectory(wszPath, wszPath);
+ return S_OK;
+}
+
+//
+// Returns path name from a file name.
+// Example: For input "C:\Windows\System.dll" returns "C:\Windows\".
+// Warning: The input file name string might be destroyed.
+//
+// Arguments:
+// pPathString - [in] SString with file name
+//
+// pBuffer - [out] SString .
+//
+// Return Value:
+// S_OK - Output buffer contains path name.
+// other errors - If Sstring throws.
+//
+HRESULT CopySystemDirectory(const SString& pPathString,
+ SString& pbuffer)
+{
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ pbuffer.Set(pPathString);
+ SString::Iterator iter = pbuffer.End();
+ if (pbuffer.FindBack(iter,DIRECTORY_SEPARATOR_CHAR_W))
+ {
+ iter++;
+ pbuffer.Truncate(iter);
+ }
+ else
+ {
+ hr = E_UNEXPECTED;
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
diff --git a/src/coreclr/utilcode/md5.cpp b/src/coreclr/utilcode/md5.cpp
new file mode 100644
index 00000000000..7297114f21f
--- /dev/null
+++ b/src/coreclr/utilcode/md5.cpp
@@ -0,0 +1,308 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// md5.cpp
+//
+
+//
+
+#include "stdafx.h"
+
+#include <stdlib.h>
+#include "stdmacros.h"
+#include "md5.h"
+#include "contract.h"
+
+void MD5::Init(BOOL fConstructed)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ // These two fields are read only, and so initialization thereof can be
+ // omitted on the second and subsequent hashes using this same instance.
+ //
+ if (!fConstructed)
+ {
+ memset(m_padding, 0, 64);
+ m_padding[0]=0x80;
+ }
+
+ m_cbitHashed = 0;
+ m_cbData = 0;
+ u.m_a = 0x67452301; // magic
+ u.m_b = 0xefcdab89; // ... constants
+ u.m_c = 0x98badcfe; // ... per
+ u.m_d = 0x10325476; // .. RFC1321
+ }
+
+
+void MD5::HashMore(const void* pvInput, ULONG cbInput)
+// Hash the additional data into the state
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ const BYTE* pbInput = (const BYTE*)pvInput;
+
+ m_cbitHashed += (((ULONGLONG)cbInput) <<3);
+
+ ULONG cbRemaining = 64 - m_cbData;
+ if (cbInput < cbRemaining)
+ {
+ // It doesn't fill up the buffer, so just store it
+ memcpy(&m_data[m_cbData], pbInput, cbInput);
+ m_cbData += cbInput;
+ }
+ else
+ {
+ // It does fill up the buffer. Fill up all that it will take
+ memcpy(&m_data[m_cbData], pbInput, cbRemaining);
+
+ // Hash the now-full buffer
+ MD5Transform(m_state, (ULONG*)&m_data[0]);
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:22019) // Suppress this OACR warning 22019:
+ // 'cbInput-=cbRemaining' may be greater than 'cbInput'. This can be caused by integer underflow.
+ // This could yield an incorrect loop index 'cbInput>=64'
+ // We only enter the else clause here if cbInput >= cbRemaining
+#endif
+ cbInput -= cbRemaining;
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+ pbInput += cbRemaining;
+
+ // Hash the data in 64-byte runs, starting just after what we've copied
+ while (cbInput >= 64)
+ {
+ if (IS_ALIGNED(pbInput, sizeof(ULONG)))
+ {
+ MD5Transform(m_state, (ULONG*)pbInput);
+ }
+ else
+ {
+ ULONG inputCopy[64 / sizeof(ULONG)];
+ memcpy(inputCopy, pbInput, sizeof(inputCopy));
+ MD5Transform(m_state, inputCopy);
+ }
+ pbInput += 64;
+ cbInput -= 64;
+ }
+
+ // Store the tail of the input into the buffer
+ memcpy(&m_data[0], pbInput, cbInput);
+ m_cbData = cbInput;
+ }
+ }
+
+
+void MD5::GetHashValue(MD5HASHDATA* phash)
+// Finalize the hash by appending the necessary padding and length count. Then
+// return the final hash value.
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ union {
+ ULONGLONG cbitHashed;
+ BYTE rgb[8];
+ }u;
+
+ // Remember how many bits there were in the input data
+ u.cbitHashed = m_cbitHashed;
+
+ // Calculate amount of padding needed. Enough so total byte count hashed is 56 mod 64
+ ULONG cbPad = (m_cbData < 56 ? 56-m_cbData : 120-m_cbData);
+
+ // Hash the padding
+ HashMore(&m_padding[0], cbPad);
+
+ // Hash the (before padding) bit length
+ HashMore(&u.rgb[0], 8);
+
+ // Return the hash value
+ memcpy(phash, &this->u.m_a, 16);
+ }
+
+
+
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // ROTATE_LEFT should be a macro that updates its first operand
+ // with its present value rotated left by the amount of its
+ // second operand, which is always a constant.
+ //
+ // One way to portably do it would be
+ //
+ // #define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+ // #define ROTATE_LEFT(x,n) (x) = ROL(x,n)
+ //
+ // but our compiler has an intrinsic!
+
+ #if (defined(HOST_X86) || defined(HOST_ARM)) && defined(TARGET_UNIX)
+ #define ROL(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+ #define ROTATE_LEFT(x,n) (x) = ROL(x,n)
+ #else
+ #define ROTATE_LEFT(x,n) (x) = _lrotl(x,n)
+ #endif
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // Constants used in each of the various rounds
+
+ #define MD5_S11 7
+ #define MD5_S12 12
+ #define MD5_S13 17
+ #define MD5_S14 22
+ #define MD5_S21 5
+ #define MD5_S22 9
+ #define MD5_S23 14
+ #define MD5_S24 20
+ #define MD5_S31 4
+ #define MD5_S32 11
+ #define MD5_S33 16
+ #define MD5_S34 23
+ #define MD5_S41 6
+ #define MD5_S42 10
+ #define MD5_S43 15
+ #define MD5_S44 21
+
+ ////////////////////////////////////////////////////////////////
+ //
+ // The core twiddle functions
+
+// #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) // the function per the standard
+ #define F(x, y, z) ((((z) ^ (y)) & (x)) ^ (z)) // an alternate encoding
+
+// #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) // the function per the standard
+ #define G(x, y, z) ((((x) ^ (y)) & (z)) ^ (y)) // an alternate encoding
+
+ #define H(x, y, z) ((x) ^ (y) ^ (z))
+
+ #define I(x, y, z) ((y) ^ ((x) | (~(z))))
+
+ #define AC(ac) ((ULONG)(ac))
+
+ ////////////////////////////////////////////////////////////////
+
+ #define FF(a, b, c, d, x, s, ac) { \
+ (a) += F (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define GG(a, b, c, d, x, s, ac) { \
+ (a) += G (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define HH(a, b, c, d, x, s, ac) { \
+ (a) += H (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ ////////////////////////////////////////////////////////////////
+
+ #define II(a, b, c, d, x, s, ac) { \
+ (a) += I (b,c,d) + (x) + (AC(ac)); \
+ ROTATE_LEFT (a, s); \
+ (a) += (b); \
+ }
+
+ void __stdcall MD5Transform(ULONG state[4], const ULONG* data)
+ {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ _ASSERTE(IS_ALIGNED(data, sizeof(ULONG)));
+
+ ULONG a=state[0];
+ ULONG b=state[1];
+ ULONG c=state[2];
+ ULONG d=state[3];
+
+ // Round 1
+ FF (a, b, c, d, data[ 0], MD5_S11, 0xd76aa478); // 1
+ FF (d, a, b, c, data[ 1], MD5_S12, 0xe8c7b756); // 2
+ FF (c, d, a, b, data[ 2], MD5_S13, 0x242070db); // 3
+ FF (b, c, d, a, data[ 3], MD5_S14, 0xc1bdceee); // 4
+ FF (a, b, c, d, data[ 4], MD5_S11, 0xf57c0faf); // 5
+ FF (d, a, b, c, data[ 5], MD5_S12, 0x4787c62a); // 6
+ FF (c, d, a, b, data[ 6], MD5_S13, 0xa8304613); // 7
+ FF (b, c, d, a, data[ 7], MD5_S14, 0xfd469501); // 8
+ FF (a, b, c, d, data[ 8], MD5_S11, 0x698098d8); // 9
+ FF (d, a, b, c, data[ 9], MD5_S12, 0x8b44f7af); // 10
+ FF (c, d, a, b, data[10], MD5_S13, 0xffff5bb1); // 11
+ FF (b, c, d, a, data[11], MD5_S14, 0x895cd7be); // 12
+ FF (a, b, c, d, data[12], MD5_S11, 0x6b901122); // 13
+ FF (d, a, b, c, data[13], MD5_S12, 0xfd987193); // 14
+ FF (c, d, a, b, data[14], MD5_S13, 0xa679438e); // 15
+ FF (b, c, d, a, data[15], MD5_S14, 0x49b40821); // 16
+
+ // Round 2
+ GG (a, b, c, d, data[ 1], MD5_S21, 0xf61e2562); // 17
+ GG (d, a, b, c, data[ 6], MD5_S22, 0xc040b340); // 18
+ GG (c, d, a, b, data[11], MD5_S23, 0x265e5a51); // 19
+ GG (b, c, d, a, data[ 0], MD5_S24, 0xe9b6c7aa); // 20
+ GG (a, b, c, d, data[ 5], MD5_S21, 0xd62f105d); // 21
+ GG (d, a, b, c, data[10], MD5_S22, 0x2441453); // 22
+ GG (c, d, a, b, data[15], MD5_S23, 0xd8a1e681); // 23
+ GG (b, c, d, a, data[ 4], MD5_S24, 0xe7d3fbc8); // 24
+ GG (a, b, c, d, data[ 9], MD5_S21, 0x21e1cde6); // 25
+ GG (d, a, b, c, data[14], MD5_S22, 0xc33707d6); // 26
+ GG (c, d, a, b, data[ 3], MD5_S23, 0xf4d50d87); // 27
+ GG (b, c, d, a, data[ 8], MD5_S24, 0x455a14ed); // 28
+ GG (a, b, c, d, data[13], MD5_S21, 0xa9e3e905); // 29
+ GG (d, a, b, c, data[ 2], MD5_S22, 0xfcefa3f8); // 30
+ GG (c, d, a, b, data[ 7], MD5_S23, 0x676f02d9); // 31
+ GG (b, c, d, a, data[12], MD5_S24, 0x8d2a4c8a); // 32
+
+ // Round 3
+ HH (a, b, c, d, data[ 5], MD5_S31, 0xfffa3942); // 33
+ HH (d, a, b, c, data[ 8], MD5_S32, 0x8771f681); // 34
+ HH (c, d, a, b, data[11], MD5_S33, 0x6d9d6122); // 35
+ HH (b, c, d, a, data[14], MD5_S34, 0xfde5380c); // 36
+ HH (a, b, c, d, data[ 1], MD5_S31, 0xa4beea44); // 37
+ HH (d, a, b, c, data[ 4], MD5_S32, 0x4bdecfa9); // 38
+ HH (c, d, a, b, data[ 7], MD5_S33, 0xf6bb4b60); // 39
+ HH (b, c, d, a, data[10], MD5_S34, 0xbebfbc70); // 40
+ HH (a, b, c, d, data[13], MD5_S31, 0x289b7ec6); // 41
+ HH (d, a, b, c, data[ 0], MD5_S32, 0xeaa127fa); // 42
+ HH (c, d, a, b, data[ 3], MD5_S33, 0xd4ef3085); // 43
+ HH (b, c, d, a, data[ 6], MD5_S34, 0x4881d05); // 44
+ HH (a, b, c, d, data[ 9], MD5_S31, 0xd9d4d039); // 45
+ HH (d, a, b, c, data[12], MD5_S32, 0xe6db99e5); // 46
+ HH (c, d, a, b, data[15], MD5_S33, 0x1fa27cf8); // 47
+ HH (b, c, d, a, data[ 2], MD5_S34, 0xc4ac5665); // 48
+
+ // Round 4
+ II (a, b, c, d, data[ 0], MD5_S41, 0xf4292244); // 49
+ II (d, a, b, c, data[ 7], MD5_S42, 0x432aff97); // 50
+ II (c, d, a, b, data[14], MD5_S43, 0xab9423a7); // 51
+ II (b, c, d, a, data[ 5], MD5_S44, 0xfc93a039); // 52
+ II (a, b, c, d, data[12], MD5_S41, 0x655b59c3); // 53
+ II (d, a, b, c, data[ 3], MD5_S42, 0x8f0ccc92); // 54
+ II (c, d, a, b, data[10], MD5_S43, 0xffeff47d); // 55
+ II (b, c, d, a, data[ 1], MD5_S44, 0x85845dd1); // 56
+ II (a, b, c, d, data[ 8], MD5_S41, 0x6fa87e4f); // 57
+ II (d, a, b, c, data[15], MD5_S42, 0xfe2ce6e0); // 58
+ II (c, d, a, b, data[ 6], MD5_S43, 0xa3014314); // 59
+ II (b, c, d, a, data[13], MD5_S44, 0x4e0811a1); // 60
+ II (a, b, c, d, data[ 4], MD5_S41, 0xf7537e82); // 61
+ II (d, a, b, c, data[11], MD5_S42, 0xbd3af235); // 62
+ II (c, d, a, b, data[ 2], MD5_S43, 0x2ad7d2bb); // 63
+ II (b, c, d, a, data[ 9], MD5_S44, 0xeb86d391); // 64
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ }
diff --git a/src/coreclr/utilcode/memorypool.cpp b/src/coreclr/utilcode/memorypool.cpp
new file mode 100644
index 00000000000..ec2dcd24c85
--- /dev/null
+++ b/src/coreclr/utilcode/memorypool.cpp
@@ -0,0 +1,316 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+#include "memorypool.h"
+#include "ex.h"
+
+size_t MemoryPool::GetSize()
+{
+ LIMITED_METHOD_CONTRACT;
+ size_t retval=0;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements;
+ block = block->next;
+ }
+ return retval;
+}
+
+#ifndef DACCESS_COMPILE
+
+BOOL MemoryPool::AddBlock(SIZE_T elementCount)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT(return FALSE;);
+ } CONTRACTL_END;
+
+ //
+ // Check for arithmetic overflow
+ //
+ S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize);
+ S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize;
+ if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow())
+ return FALSE;
+
+ //
+ // Allocate the new block.
+ //
+
+ Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()];
+
+ if (block == NULL)
+ return FALSE;
+
+ //
+ // Chain all elements together for the free list
+ //
+
+ _ASSERTE(m_freeList == NULL);
+ Element **prev = &m_freeList;
+
+ Element *e = block->elements;
+ Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize);
+ while (e < eEnd)
+ {
+ *prev = e;
+ prev = &e->next;
+#if _DEBUG
+ DeadBeef(e);
+#endif
+ e = (Element*) ((BYTE*)e + m_elementSize);
+ }
+
+ *prev = NULL;
+
+ //
+ // Initialize the other block fields & link the block into the block list
+ //
+
+ block->elementsEnd = e;
+ block->next = m_blocks;
+ m_blocks = block;
+
+ return TRUE;
+}
+
+void MemoryPool::DeadBeef(Element *element)
+{
+#if _DEBUG
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ int *i = &element->deadBeef;
+ int *iEnd = (int*) ((BYTE*)element + m_elementSize);
+ while (i < iEnd)
+ *i++ = (int) 0xdeadbeef;
+#else
+ LIMITED_METHOD_CONTRACT;
+#endif
+}
+
+MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount)
+ : m_elementSize(elementSize),
+ m_growCount(initGrowth),
+ m_blocks(NULL),
+ m_freeList(NULL)
+{
+ CONTRACTL {
+ if (initCount) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(elementSize >= sizeof(Element));
+ _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0);
+
+ if (initCount > 0)
+ AddBlock(initCount);
+}
+
+MemoryPool::~MemoryPool()
+{
+ LIMITED_METHOD_CONTRACT;
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] (BYTE*) block;
+ block = next;
+ }
+}
+
+BOOL MemoryPool::IsElement(void *element)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ if (element >= block->elements &&
+ element < block->elementsEnd )
+ {
+ return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0;
+ }
+ block = block->next;
+ }
+
+ return FALSE;
+}
+
+BOOL MemoryPool::IsAllocatedElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+ if (!IsElement(element))
+ return FALSE;
+
+ //
+ // Now, make sure the element isn't
+ // in the free list.
+ //
+
+#if _DEBUG
+ //
+ // In a debug build, all objects on the free list
+ // will be marked with deadbeef. This means that
+ // if the object is not deadbeef, it's not on the
+ // free list.
+ //
+ // This check will give us decent performance in
+ // a debug build for FreeElement, since we
+ // always expect to return TRUE in that case.
+ //
+
+ if (((Element*)element)->deadBeef != (int) 0xdeadBeef)
+ return TRUE;
+#endif
+
+ Element *f = m_freeList;
+ while (f != NULL)
+ {
+ if (f == element)
+ return FALSE;
+ f = f->next;
+ }
+
+#if _DEBUG
+ //
+ // We should never get here in a debug build, because
+ // all free elements should be deadbeefed.
+ //
+ _ASSERTE(!"Unreachable");
+#endif
+
+ return TRUE;
+}
+
+void *MemoryPool::AllocateElement()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ void *element = AllocateElementNoThrow();
+ if (element == NULL)
+ ThrowOutOfMemory();
+
+ return element;
+}
+
+void *MemoryPool::AllocateElementNoThrow()
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ INJECT_FAULT( return FALSE; );
+ } CONTRACTL_END;
+
+ void *element = m_freeList;
+
+ if (element == NULL)
+ {
+ if (!AddBlock(m_growCount))
+ return NULL;
+
+ m_growCount *= 2;
+ element = m_freeList;
+ }
+
+ // if we come there means that addblock succeeded and m_freelist isn't null anymore
+ PREFIX_ASSUME(m_freeList!= NULL);
+ m_freeList = m_freeList->next;
+
+ return element;
+}
+
+void MemoryPool::FreeElement(void *element)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ } CONTRACTL_END;
+
+#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive
+ _ASSERTE(IsAllocatedElement(element));
+#endif
+
+ Element *e = (Element *) element;
+
+#if _DEBUG
+ DeadBeef(e);
+#endif
+
+ e->next = m_freeList;
+ m_freeList = e;
+}
+
+void MemoryPool::FreeAllElements()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ Block *block = m_blocks;
+ while (block != NULL)
+ {
+ Block *next = block->next;
+ delete [] block;
+ block = next;
+ }
+
+ m_freeList = NULL;
+ m_blocks = NULL;
+}
+
+
+
+MemoryPool::Iterator::Iterator(MemoryPool *pool)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ //
+ // Warning! This only works if you haven't freed
+ // any elements.
+ //
+
+ m_next = pool->m_blocks;
+ m_e = NULL;
+ m_eEnd = NULL;
+ m_end = (BYTE*) pool->m_freeList;
+ m_size = pool->m_elementSize;
+}
+
+BOOL MemoryPool::Iterator::Next()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ if (m_e == m_eEnd
+ || (m_e == m_end && m_end != NULL))
+ {
+ if (m_next == NULL)
+ return FALSE;
+ m_e = (BYTE*) m_next->elements;
+ m_eEnd = (BYTE*) m_next->elementsEnd;
+ m_next = m_next->next;
+ if (m_e == m_end)
+ return FALSE;
+ }
+
+ m_e += m_size;
+
+ return TRUE;
+}
+
+#endif
diff --git a/src/coreclr/utilcode/namespaceutil.cpp b/src/coreclr/utilcode/namespaceutil.cpp
new file mode 100644
index 00000000000..d930c6f86d2
--- /dev/null
+++ b/src/coreclr/utilcode/namespaceutil.cpp
@@ -0,0 +1,678 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// NamespaceUtil.cpp
+//
+
+//
+// Helpers for converting namespace separators.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "corhdr.h"
+#include "corhlpr.h"
+#include "sstring.h"
+#include "utilcode.h"
+
+#ifndef _ASSERTE
+#define _ASSERTE(foo)
+#endif
+
+#include "nsutilpriv.h"
+
+
+//*****************************************************************************
+// Determine how many chars large a fully qualified name would be given the
+// two parts of the name. The return value includes room for every character
+// in both names, as well as room for the separator and a final terminator.
+//*****************************************************************************
+int ns::GetFullLength( // Number of chars in full name.
+ const WCHAR *szNameSpace, // Namspace for value.
+ const WCHAR *szName) // Name of value.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ int iLen = 1; // Null terminator.
+ if (szNameSpace)
+ iLen += (int)wcslen(szNameSpace);
+ if (szName)
+ iLen += (int)wcslen(szName);
+ if (szNameSpace && *szNameSpace && szName && *szName)
+ ++iLen;
+ return iLen;
+} //int ns::GetFullLength()
+
+int ns::GetFullLength( // Number of chars in full name.
+ LPCUTF8 szNameSpace, // Namspace for value.
+ LPCUTF8 szName) // Name of value.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+
+ int iLen = 1;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ if (szNameSpace && *szNameSpace && szName && *szName)
+ ++iLen;
+ return iLen;
+} //int ns::GetFullLength()
+
+
+//*****************************************************************************
+// Scan the string from the rear looking for the first valid separator. If
+// found, return a pointer to it. Else return null. This code is smart enough
+// to skip over special sequences, such as:
+// a.b..ctor
+// ^
+// |
+// The ".ctor" is considered one token.
+//*****************************************************************************
+WCHAR *ns::FindSep( // Pointer to separator or null.
+ const WCHAR *szPath) // The path to look in.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ _ASSERTE(szPath);
+ WCHAR *ptr = (WCHAR*)wcsrchr(szPath, NAMESPACE_SEPARATOR_WCHAR);
+ if((ptr == NULL) || (ptr == szPath)) return NULL;
+ if(*(ptr - 1) == NAMESPACE_SEPARATOR_WCHAR) // here ptr is at least szPath+1
+ --ptr;
+ return ptr;
+} //WCHAR *ns::FindSep()
+
+//<TODO>@todo: this isn't dbcs safe if this were ansi, but this is utf8. Still an issue?</TODO>
+LPUTF8 ns::FindSep( // Pointer to separator or null.
+ LPCUTF8 szPath) // The path to look in.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ _ASSERTE(szPath);
+ LPUTF8 ptr = const_cast<LPUTF8>(strrchr(szPath, NAMESPACE_SEPARATOR_CHAR));
+ if((ptr == NULL) || (ptr == szPath)) return NULL;
+ if(*(ptr - 1) == NAMESPACE_SEPARATOR_CHAR) // here ptr is at least szPath+1
+ --ptr;
+ return ptr;
+} //LPUTF8 ns::FindSep()
+
+
+
+//*****************************************************************************
+// Take a path and find the last separator (nsFindSep), and then replace the
+// separator with a '\0' and return a pointer to the name. So for example:
+// a.b.c
+// becomes two strings "a.b" and "c" and the return value points to "c".
+//*****************************************************************************
+WCHAR *ns::SplitInline( // Pointer to name portion.
+ __inout __inout_z WCHAR *szPath) // The path to split.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ WCHAR *ptr = ns::FindSep(szPath);
+ if (ptr)
+ {
+ *ptr = 0;
+ ++ptr;
+ }
+ return ptr;
+} // WCHAR *ns::SplitInline()
+
+LPUTF8 ns::SplitInline( // Pointer to name portion.
+ __inout __inout_z LPUTF8 szPath) // The path to split.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPUTF8 ptr = ns::FindSep(szPath);
+ if (ptr)
+ {
+ *ptr = 0;
+ ++ptr;
+ }
+ return ptr;
+} // LPUTF8 ns::SplitInline()
+
+void ns::SplitInline(
+ __inout __inout_z LPWSTR szPath, // Path to split.
+ LPCWSTR &szNameSpace, // Return pointer to namespace.
+ LPCWSTR &szName) // Return pointer to name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ WCHAR *ptr = SplitInline(szPath);
+ if (ptr)
+ {
+ szNameSpace = szPath;
+ szName = ptr;
+ }
+ else
+ {
+ szNameSpace = 0;
+ szName = szPath;
+ }
+} // void ns::SplitInline()
+
+void ns::SplitInline(
+ __inout __inout_z LPUTF8 szPath, // Path to split.
+ LPCUTF8 &szNameSpace, // Return pointer to namespace.
+ LPCUTF8 &szName) // Return pointer to name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPUTF8 ptr = SplitInline(szPath);
+ if (ptr)
+ {
+ szNameSpace = szPath;
+ szName = ptr;
+ }
+ else
+ {
+ szNameSpace = 0;
+ szName = szPath;
+ }
+} // void ns::SplitInline()
+
+
+//*****************************************************************************
+// Split the last parsable element from the end of the string as the name,
+// the first part as the namespace.
+//*****************************************************************************
+int ns::SplitPath( // true ok, false trunction.
+ const WCHAR *szPath, // Path to split.
+ __out_ecount(cchNameSpace) WCHAR *szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ __out_ecount(cchName) WCHAR *szName, // Output for name.
+ int cchName) // Max chars for output.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ const WCHAR *ptr = ns::FindSep(szPath);
+ size_t iLen = (ptr) ? ptr - szPath : 0;
+ size_t iCopyMax;
+ int brtn = true;
+ if (szNameSpace && cchNameSpace)
+ {
+ _ASSERTE(cchNameSpace > 1);
+ iCopyMax = cchNameSpace - 1;
+ iCopyMax = min(iCopyMax, iLen);
+ wcsncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
+ szNameSpace[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchNameSpace)
+ brtn = false;
+ }
+
+ if (szName && cchName)
+ {
+ _ASSERTE(cchName > 1);
+ iCopyMax = cchName - 1;
+ if (ptr)
+ ++ptr;
+ else
+ ptr = szPath;
+ iLen = (int)wcslen(ptr);
+ iCopyMax = min(iCopyMax, iLen);
+ wcsncpy_s(szName, cchName, ptr, iCopyMax);
+ szName[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchName)
+ brtn = false;
+ }
+ return brtn;
+} // int ns::SplitPath()
+
+
+int ns::SplitPath( // true ok, false trunction.
+ LPCUTF8 szPath, // Path to split.
+ __out_ecount_opt (cchNameSpace) LPUTF8 szNameSpace, // Output for namespace value.
+ int cchNameSpace, // Max chars for output.
+ __out_ecount_opt (cchName) LPUTF8 szName, // Output for name.
+ int cchName) // Max chars for output.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPCUTF8 ptr = ns::FindSep(szPath);
+ size_t iLen = (ptr) ? ptr - szPath : 0;
+ size_t iCopyMax;
+ int brtn = true;
+ if (szNameSpace && cchNameSpace)
+ {
+ _ASSERTE(cchNameSpace > 1);
+ iCopyMax = cchNameSpace-1;
+ iCopyMax = min(iCopyMax, iLen);
+ strncpy_s(szNameSpace, cchNameSpace, szPath, iCopyMax);
+ szNameSpace[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchNameSpace)
+ brtn = false;
+ }
+
+ if (szName && cchName)
+ {
+ _ASSERTE(cchName > 1);
+ iCopyMax = cchName-1;
+ if (ptr)
+ ++ptr;
+ else
+ ptr = szPath;
+ iLen = (int)strlen(ptr);
+ iCopyMax = min(iCopyMax, iLen);
+ strncpy_s(szName, cchName, ptr, iCopyMax);
+ szName[iCopyMax] = 0;
+
+ if (iLen >= (size_t)cchName)
+ brtn = false;
+ }
+ return brtn;
+} // int ns::SplitPath()
+
+
+//*****************************************************************************
+// Take two values and put them together in a fully qualified path using the
+// correct separator.
+//*****************************************************************************
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ const WCHAR *szNameSpace, // Namespace.
+ const WCHAR *szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNameSpace && *szNameSpace != W('\0'))
+ {
+ if (wcsncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
+ return false;
+
+ // Add namespace separator if a non-empty name was supplied
+ if (szName && *szName != W('\0'))
+ {
+ if (wcsncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_WSTR, _TRUNCATE) == STRUNCATE)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (szName && *szName)
+ {
+ if (wcsncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
+ return false;
+ }
+
+ return true;
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) LPUTF8 szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNameSpace, // Namespace.
+ LPCUTF8 szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNameSpace && *szNameSpace != W('\0'))
+ {
+ if (strncpy_s(szOut, cchChars, szNameSpace, _TRUNCATE) == STRUNCATE)
+ return false;
+
+ // Add namespace separator if a non-empty name was supplied
+ if (szName && *szName != W('\0'))
+ {
+ if (strncat_s(szOut, cchChars, NAMESPACE_SEPARATOR_STR, _TRUNCATE) == STRUNCATE)
+ {
+ return false;
+ }
+ }
+ }
+
+ if (szName && *szName)
+ {
+ if (strncat_s(szOut, cchChars, szName, _TRUNCATE) == STRUNCATE)
+ return false;
+ }
+
+ return true;
+
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false truncation.
+ __out_ecount(cchChars) WCHAR *szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szNamespace, // Namespace.
+ LPCUTF8 szName) // Name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ if (szOut)
+ *szOut = 0;
+ else
+ return false;
+
+ if (szNamespace != NULL && *szNamespace != '\0')
+ {
+ if (cchChars < 2)
+ return false;
+
+ int count;
+
+ // We use cBuffer - 2 to account for the '.' and at least a 1 character name below.
+ count = WszMultiByteToWideChar(CP_UTF8, 0, szNamespace, -1, szOut, cchChars-2);
+ if (count == 0)
+ return false; // Supply a bigger buffer!
+
+ // buffer access is bounded: WszMultiByteToWideChar returns 0 if access doesn't fit in range
+#ifdef _PREFAST_
+ #pragma warning( suppress: 26015 )
+#endif
+ szOut[count-1] = NAMESPACE_SEPARATOR_WCHAR;
+ szOut += count;
+ cchChars -= count;
+ }
+
+ if (((cchChars == 0) && (szName != NULL) && (*szName != '\0')) ||
+ (WszMultiByteToWideChar(CP_UTF8, 0, szName, -1, szOut, cchChars) == 0))
+ return false; // supply a bigger buffer!
+ return true;
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ LPCUTF8 szNameSpace, // Namespace for name.
+ LPCUTF8 szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickArray<WCHAR> &qa, // Where to put results.
+ LPCUTF8 szNameSpace, // Namespace for name.
+ LPCUTF8 szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)strlen(szNameSpace);
+ if (szName)
+ iLen += (int)strlen(szName);
+ WCHAR *szOut = (WCHAR *) qa.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+int ns::MakePath( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ const WCHAR *szNameSpace, // Namespace for name.
+ const WCHAR *szName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iLen = 2;
+ if (szNameSpace)
+ iLen += (int)wcslen(szNameSpace);
+ if (szName)
+ iLen += (int)wcslen(szName);
+ WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
+ if (!szOut)
+ return false;
+ return ns::MakePath(szOut, iLen, szNameSpace, szName);
+} // int ns::MakePath()
+
+void ns::MakePath( // throws on out of memory
+ SString &ssBuf, // Where to put results.
+ const SString &ssNameSpace, // Namespace for name.
+ const SString &ssName) // Final part of name.
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ ssBuf.Clear();
+
+ if (!ssNameSpace.IsEmpty())
+ {
+ if (ssName.IsEmpty())
+ {
+ ssBuf.Set(ssNameSpace);
+ }
+ else
+ {
+ SString s(SString::Literal, NAMESPACE_SEPARATOR_WSTR);
+ ssBuf.Set(ssNameSpace, s);
+ }
+ }
+
+ if (!ssName.IsEmpty())
+ {
+ ssBuf.Append(ssName);
+ }
+}
+
+bool ns::MakeAssemblyQualifiedName( // true ok, false truncation
+ __out_ecount(dwBuffer) WCHAR* pBuffer, // Buffer to recieve the results
+ int dwBuffer, // Number of characters total in buffer
+ const WCHAR *szTypeName, // Namespace for name.
+ int dwTypeName, // Number of characters (not including null)
+ const WCHAR *szAssemblyName, // Final part of name.
+ int dwAssemblyName) // Number of characters (not including null)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (dwBuffer < 2)
+ return false;
+
+ int iCopyMax = 0;
+ _ASSERTE(pBuffer);
+ *pBuffer = NULL;
+
+ if (szTypeName && *szTypeName != W('\0'))
+ {
+ _ASSERTE(dwTypeName > 0);
+ iCopyMax = min(dwBuffer-1, dwTypeName);
+ wcsncpy_s(pBuffer, dwBuffer, szTypeName, iCopyMax);
+ dwBuffer -= iCopyMax;
+ }
+
+ if (szAssemblyName && *szAssemblyName != W('\0'))
+ {
+
+ if(dwBuffer < ASSEMBLY_SEPARATOR_LEN)
+ return false;
+
+ for(DWORD i = 0; i < ASSEMBLY_SEPARATOR_LEN; i++)
+ pBuffer[iCopyMax+i] = ASSEMBLY_SEPARATOR_WSTR[i];
+
+ dwBuffer -= ASSEMBLY_SEPARATOR_LEN;
+ if(dwBuffer == 0)
+ return false;
+
+ int iCur = iCopyMax + ASSEMBLY_SEPARATOR_LEN;
+ _ASSERTE(dwAssemblyName > 0);
+ iCopyMax = min(dwBuffer-1, dwAssemblyName);
+ wcsncpy_s(pBuffer + iCur, dwBuffer, szAssemblyName, iCopyMax);
+ pBuffer[iCur + iCopyMax] = W('\0');
+
+ if (iCopyMax < dwAssemblyName)
+ return false;
+ }
+ else {
+ if(dwBuffer == 0) {
+ PREFIX_ASSUME(iCopyMax > 0);
+ pBuffer[iCopyMax-1] = W('\0');
+ return false;
+ }
+ else
+ pBuffer[iCopyMax] = W('\0');
+ }
+
+ return true;
+} // int ns::MakePath()
+
+bool ns::MakeAssemblyQualifiedName( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ const WCHAR *szTypeName, // Namespace for name.
+ const WCHAR *szAssemblyName) // Final part of name.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ int iTypeName = 0;
+ int iAssemblyName = 0;
+ if (szTypeName)
+ iTypeName = (int)wcslen(szTypeName);
+ if (szAssemblyName)
+ iAssemblyName = (int)wcslen(szAssemblyName);
+
+ int iLen = ASSEMBLY_SEPARATOR_LEN + iTypeName + iAssemblyName + 1; // Space for null terminator
+ WCHAR *szOut = (WCHAR *) qb.AllocNoThrow(iLen * sizeof(WCHAR));
+ if (!szOut)
+ return false;
+
+ bool ret;
+ ret = ns::MakeAssemblyQualifiedName(szOut, iLen, szTypeName, iTypeName, szAssemblyName, iAssemblyName);
+ _ASSERTE(ret);
+ return true;
+}
+
+int ns::MakeNestedTypeName( // true ok, false out of memory
+ CQuickBytes &qb, // Where to put results.
+ LPCUTF8 szEnclosingName, // Full name for enclosing type
+ LPCUTF8 szNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+ _ASSERTE(szEnclosingName && szNestedName);
+ int iLen = 2;
+ iLen += (int)strlen(szEnclosingName);
+ iLen += (int)strlen(szNestedName);
+ LPUTF8 szOut = (LPUTF8) qb.AllocNoThrow(iLen);
+ if (!szOut)
+ return false;
+ return ns::MakeNestedTypeName(szOut, iLen, szEnclosingName, szNestedName);
+} // int ns::MakeNestedTypeName()
+
+int ns::MakeNestedTypeName( // true ok, false truncation.
+ __out_ecount (cchChars) LPUTF8 szOut, // output path for name.
+ int cchChars, // max chars for output path.
+ LPCUTF8 szEnclosingName, // Full name for enclosing type
+ LPCUTF8 szNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (cchChars < 1)
+ return false;
+
+ int iCopyMax = 0, iLen;
+ int brtn = true;
+ *szOut = 0;
+
+ iLen = (int)strlen(szEnclosingName);
+ iCopyMax = min(cchChars-1, iLen);
+ strncpy_s(szOut, cchChars, szEnclosingName, iCopyMax);
+
+ if (iLen >= cchChars)
+ brtn = false;
+
+ szOut[iCopyMax] = NESTED_SEPARATOR_CHAR;
+ int iCur = iCopyMax+1; // iCopyMax characters + nested_separator_char
+ cchChars -= iCur;
+ if(cchChars == 0)
+ return false;
+
+ iLen = (int)strlen(szNestedName);
+ iCopyMax = min(cchChars-1, iLen);
+ strncpy_s(&szOut[iCur], cchChars, szNestedName, iCopyMax);
+ szOut[iCur + iCopyMax] = 0;
+
+ if (iLen >= cchChars)
+ brtn = false;
+
+ return brtn;
+} // int ns::MakeNestedTypeName()
+
+void ns::MakeNestedTypeName( // throws on out of memory
+ SString &ssBuf, // output path for name.
+ const SString &ssEnclosingName, // Full name for enclosing type
+ const SString &ssNestedName) // Full name for nested type
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ ssBuf.Clear();
+
+ ssBuf.Append(ssEnclosingName);
+ ssBuf.Append(NESTED_SEPARATOR_WCHAR);
+ ssBuf.Append(ssNestedName);
+}
+
diff --git a/src/coreclr/utilcode/opinfo.cpp b/src/coreclr/utilcode/opinfo.cpp
new file mode 100644
index 00000000000..0a2efdf28f1
--- /dev/null
+++ b/src/coreclr/utilcode/opinfo.cpp
@@ -0,0 +1,121 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/***************************************************************************/
+/* OpInfo.cpp */
+/***************************************************************************/
+
+#include "stdafx.h"
+#include <cor.h> // for debugMacros.h
+#include "debugmacros.h" // for ASSERTE
+#include "opinfo.h"
+
+
+OpInfo::OpInfoData OpInfo::table[] = {
+
+#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) \
+ { s, (OPCODE_FORMAT) (args + type), FLOW_ ## ctrl, pop, push, c },
+
+ // Kind of a workaround, get the prefixes (IInternal) to return InlineOpcode instead of InlineNone
+#define IInternal (InlineOpcode - InlineNone)
+#define IMacro 0
+#define IPrimitive 0
+#define IAnnotation 0
+#define IObjModel 0
+#define IPrefix 0
+
+#define Pop0 0
+#define Pop1 1
+#define PopI 1
+#define PopI4 1
+#define PopR4 1
+#define PopI8 1
+#define PopR8 1
+#define PopRef 1
+#define VarPop -1
+
+#define Push0 0
+#define Push1 1
+#define PushI 1
+#define PushI4 1
+#define PushR4 1
+#define PushI8 1
+#define PushR8 1
+#define PushRef 1
+#define VarPush -1
+
+#include "opcode.def"
+#undef OPDEF
+};
+
+
+/***************************************************************************/
+/* parse instruction at 'instrPtr', into its opcode (OpInfo), and its
+ (inline)args, 'args' 'instrPtr' is updated */
+
+/***************************************************************************/
+const BYTE* OpInfo::fetch(const BYTE* instrPtr, OpArgsVal* args) {
+
+ data = &table[*instrPtr++];
+AGAIN:
+ _ASSERTE(data - table == data->opcode);
+ switch(data->format) {
+ case InlineNone:
+ break;
+ case InlineOpcode:
+ _ASSERTE(*instrPtr + 256 < (int) (sizeof(table) / sizeof(OpInfoData)));
+ data = &table[256 + *instrPtr++];
+ goto AGAIN;
+
+ case ShortInlineVar:
+ args->i = *instrPtr; instrPtr +=1;
+ break;
+ case InlineVar:
+ args->i = GET_UNALIGNED_VAL16(instrPtr); instrPtr +=2;
+ break;
+ case ShortInlineI:
+ case ShortInlineBrTarget:
+ args->i = *instrPtr; instrPtr +=1;
+ break;
+ case ShortInlineR: {
+ DWORD f = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ args->r = *((float*) (&f));
+ }
+ break;
+ case InlineRVA:
+ case InlineI:
+ case InlineMethod:
+ case InlineField:
+ case InlineType:
+ case InlineString:
+ case InlineSig:
+ case InlineTok:
+ case InlineBrTarget:
+ args->i = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ break;
+ case InlineI8:
+ args->i8 = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8;
+ break;
+ case InlineR: {
+ __int64 d = GET_UNALIGNED_VAL64(instrPtr); instrPtr +=8;
+ args->r = *((double*) (&d));
+ } break;
+ case InlineSwitch:
+ args->switch_.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=4;
+ args->switch_.targets = (int*) instrPtr; instrPtr += (4 * args->switch_.count);
+ break;
+ case InlinePhi:
+ args->phi.count = GET_UNALIGNED_VAL32(instrPtr); instrPtr +=1;
+ args->phi.vars = (unsigned short*) instrPtr; instrPtr += (2 * args->phi.count);
+ break;
+ default:
+#ifdef _DEBUG
+ _ASSERTE(!"BadType");
+#else
+ __assume(0); // we are really certain the default case does not happen
+#endif
+ break;
+ }
+ return(instrPtr);
+}
+
diff --git a/src/coreclr/utilcode/outstring.cpp b/src/coreclr/utilcode/outstring.cpp
new file mode 100644
index 00000000000..40b8e004066
--- /dev/null
+++ b/src/coreclr/utilcode/outstring.cpp
@@ -0,0 +1,180 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*****************************************************************/
+/* OutString.cpp */
+/*****************************************************************/
+/* A simple, lightweight, character output stream, with very few
+ external dependancies (like sprintf ... ) */
+
+/*
+ Date : 2/1/99 */
+/*****************************************************************/
+
+#include "stdafx.h"
+#include "outstring.h"
+
+
+/*****************************************************************/
+// print out 'count' instances of the character 'c'
+OutString& OutString::pad(size_t count, char c) {
+ if (cur+count > end)
+ Realloc(count);
+ memset(cur, c, count);
+ cur = cur + count;
+ return(*this);
+}
+
+/*****************************************************************/
+// prints out a decimal representation
+OutString& OutString::operator<<(double d) {
+
+ if (d == 0.0) {
+ *this << "0.0";
+ return *this;
+ }
+
+ if (d < 0) {
+ d = -d;
+ *this << '-';
+ }
+
+ // compute the exponent
+ int exponent = 0;
+ while (d > 10.0) {
+ d /= 10;
+ exponent++;
+ if (exponent > 500) { // avoids a possible infinite loop
+ *this << "INF";
+ return *this;
+ }
+ }
+ while (d < 1.0) {
+ d *= 10;
+ --exponent;
+ if (exponent < -500) { // avoids a possible infinite loop
+ *this << "0.0";
+ return *this;
+ }
+ }
+
+ // we now have a normalized d (between 1 and 10)
+ double delta = .5E-10;
+ d += delta; // round to the precision we are displaying
+
+ unsigned trailingZeros = 0;
+ for(unsigned i = 0; i < 10; i++) {
+ int digit = (int) d;
+ d = (d - digit) * 10; // ISSUE: does roundoff ever bite us here?
+
+ if (digit == 0) // defer printing traiing zeros
+ trailingZeros++;
+ else {
+ if (trailingZeros > 0) {
+ this->pad(trailingZeros, '0');
+ trailingZeros = 0;
+ }
+ *this << (char) ('0' + digit);
+ }
+ if (i == 0)
+ *this << '.';
+
+ }
+ if (exponent != 0) {
+ *this << 'E';
+ *this << exponent;
+ }
+ return(*this);
+}
+
+/*****************************************************************/
+// prints out a decimal representation
+OutString& OutString::dec(int i, size_t minWidth) {
+ char buff[12]; // big enough for any number (10 digits, - sign, null term)
+ char* ptr = &buff[11];
+ *ptr = 0;
+
+ unsigned val = i;
+ if (i < 0)
+ val = -i; // note this happens to also work for minint!
+
+ for(;;) {
+ if (val < 10) {
+ *--ptr = '0' + val;
+ break;
+ }
+ *--ptr = '0' + (val % 10);
+ val = val / 10;
+ }
+
+ if (i < 0)
+ *--ptr = '-';
+
+ size_t len = &buff[11] - ptr; // length of string
+ if (len < minWidth)
+ pad(minWidth-len, ' ');
+
+ *this << ptr;
+ return(*this);
+}
+
+/*****************************************************************/
+OutString& OutString::hex(unsigned __int64 i, int minWidth, unsigned flags) {
+
+ unsigned hi = unsigned(i >> 32);
+ unsigned low = unsigned(i);
+
+ if (hi != 0) {
+ minWidth -= 8;
+ hex(hi, minWidth, flags); // print upper bits
+ flags = zeroFill;
+ minWidth = 8;
+ }
+ return hex(low, minWidth, flags); // print lower bits
+}
+
+/*****************************************************************/
+OutString& OutString::hex(unsigned i, int minWidth, unsigned flags) {
+ char buff[12]; // big enough for any number
+ char* ptr = &buff[11];
+ *ptr = 0;
+
+ static const char digits[] = "0123456789ABCDEF";
+
+ for(;;) {
+ if (i < 16) {
+ *--ptr = digits[i];
+ break;
+ }
+ *--ptr = digits[(i % 16)];
+ i = i / 16;
+ }
+
+ size_t len = &buff[11] - ptr; // length of string
+ if (flags & put0x) {
+ if (flags & zeroFill)
+ *this << "0x";
+ else
+ *--ptr = 'x', *--ptr = '0';
+ len += 2;
+ }
+
+ if (len < (size_t)minWidth)
+ pad(minWidth-len, (flags & zeroFill) ? '0' : ' ');
+
+ *this << ptr;
+ return(*this);
+}
+
+/*****************************************************************/
+void OutString::Realloc(size_t neededSpace) {
+ size_t oldSize = cur-start;
+ size_t newSize = (oldSize + neededSpace) * 3 / 2 + 32;
+ char* oldBuff = start;
+ start = new char[newSize+1];
+ memcpy(start, oldBuff, oldSize);
+ cur = &start[oldSize];
+ end = &start[newSize];
+ delete [] oldBuff;
+}
+
diff --git a/src/coreclr/utilcode/pedecoder.cpp b/src/coreclr/utilcode/pedecoder.cpp
new file mode 100644
index 00000000000..91fde64d297
--- /dev/null
+++ b/src/coreclr/utilcode/pedecoder.cpp
@@ -0,0 +1,3280 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// PEDecoder.cpp
+//
+
+// --------------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "ex.h"
+#include "pedecoder.h"
+#include "mdcommon.h"
+#include "nibblemapmacros.h"
+
+CHECK PEDecoder::CheckFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(HasContents());
+
+ if (HasNTHeaders())
+ {
+ CHECK(CheckNTHeaders());
+
+ if (HasCorHeader())
+ {
+ CHECK(CheckCorHeader());
+
+ if (IsILOnly())
+ CHECK(CheckILOnly());
+
+ if (HasNativeHeader())
+ CHECK(CheckNativeHeader());
+
+ CHECK(CheckWillCreateGuardPage());
+ }
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckNTFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckCORFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(!HasNativeHeader());
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILOnlyFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(IsILOnly());
+ CHECK(!HasNativeHeader());
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckNativeFormat() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+#ifdef FEATURE_PREJIT
+ CHECK(CheckFormat());
+ CHECK(HasNTHeaders());
+ CHECK(HasCorHeader());
+ CHECK(!IsILOnly());
+ CHECK(HasNativeHeader());
+#else // FEATURE_PREJIT
+ CHECK(false);
+#endif // FEATURE_PREJIT
+
+ CHECK_OK;
+}
+
+BOOL PEDecoder::HasNTHeaders() const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_END;
+
+ // Check for a valid DOS header
+
+ if (m_size < sizeof(IMAGE_DOS_HEADER))
+ RETURN FALSE;
+
+ IMAGE_DOS_HEADER* pDOS = PTR_IMAGE_DOS_HEADER(m_base);
+
+ {
+ if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE)
+ || (DWORD) pDOS->e_lfanew == VAL32(0))
+ {
+ RETURN FALSE;
+ }
+
+ // Check for integer overflow
+ S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) +
+ S_SIZE_T(sizeof(IMAGE_NT_HEADERS)));
+ if (cbNTHeaderEnd.IsOverflow())
+ {
+ RETURN FALSE;
+ }
+
+ // Now check for a valid NT header
+ if (m_size < cbNTHeaderEnd.Value())
+ {
+ RETURN FALSE;
+ }
+ }
+
+ IMAGE_NT_HEADERS *pNT = PTR_IMAGE_NT_HEADERS(m_base + VAL32(pDOS->e_lfanew));
+
+ if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ RETURN FALSE;
+
+ if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR32_MAGIC))
+ {
+ if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER32)))
+ RETURN FALSE;
+ }
+ else if (pNT->OptionalHeader.Magic == VAL16(IMAGE_NT_OPTIONAL_HDR64_MAGIC))
+ {
+ // on 64 bit we can promote this
+ if (pNT->FileHeader.SizeOfOptionalHeader != VAL16(sizeof(IMAGE_OPTIONAL_HEADER64)))
+ RETURN FALSE;
+
+ // Check for integer overflow
+ S_SIZE_T cbNTHeaderEnd(S_SIZE_T(static_cast<SIZE_T>(VAL32(pDOS->e_lfanew))) +
+ S_SIZE_T(sizeof(IMAGE_NT_HEADERS64)));
+
+ if (cbNTHeaderEnd.IsOverflow())
+ {
+ RETURN FALSE;
+ }
+
+ // Now check for a valid NT header
+ if (m_size < cbNTHeaderEnd.Value())
+ {
+ RETURN FALSE;
+ }
+
+ }
+ else
+ RETURN FALSE;
+
+ // Go ahead and cache NT header since we already found it.
+ const_cast<PEDecoder *>(this)->m_pNTHeaders = dac_cast<PTR_IMAGE_NT_HEADERS>(pNT);
+
+ RETURN TRUE;
+}
+
+CHECK PEDecoder::CheckNTHeaders() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ PRECONDITION(HasContents());
+ }
+ CONTRACT_CHECK_END;
+
+ // Only check once per file
+ if (m_flags & FLAG_NT_CHECKED)
+ CHECK_OK;
+
+ CHECK(HasNTHeaders());
+
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+
+ CHECK((pNT->FileHeader.Characteristics & VAL16(IMAGE_FILE_SYSTEM)) == 0);
+
+ CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.FileAlignment)));
+ CHECK(CheckAlignment(VAL32(pNT->OptionalHeader.SectionAlignment)));
+
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.FileAlignment), 512));
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SectionAlignment), VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfImage), VAL32(pNT->OptionalHeader.SectionAlignment)));
+ CHECK(CheckAligned((UINT)VAL32(pNT->OptionalHeader.SizeOfHeaders), VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ // Data directories will be validated later on.
+ PTR_IMAGE_DATA_DIRECTORY pDataDirectories = NULL;
+
+ if (Has32BitNTHeaders())
+ {
+ IMAGE_NT_HEADERS32* pNT32=GetNTHeaders32();
+ CHECK(CheckAligned(VAL32(pNT32->OptionalHeader.ImageBase), 0x10000));
+ CHECK((VAL32(pNT32->OptionalHeader.SizeOfStackCommit) <= VAL32(pNT32->OptionalHeader.SizeOfStackReserve)));
+ CHECK((VAL32(pNT32->OptionalHeader.SizeOfHeapCommit) <= VAL32(pNT32->OptionalHeader.SizeOfHeapReserve)));
+ pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(pNT32) + offsetof(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory));
+ }
+ else
+ {
+ IMAGE_NT_HEADERS64* pNT64=GetNTHeaders64();
+ CHECK(CheckAligned(VAL64(pNT64->OptionalHeader.ImageBase), 0x10000));
+ CHECK((VAL64(pNT64->OptionalHeader.SizeOfStackCommit) <= VAL64(pNT64->OptionalHeader.SizeOfStackReserve)));
+ CHECK((VAL64(pNT64->OptionalHeader.SizeOfHeapCommit) <= VAL64(pNT64->OptionalHeader.SizeOfHeapReserve)));
+ pDataDirectories = dac_cast<PTR_IMAGE_DATA_DIRECTORY>(
+ dac_cast<TADDR>(pNT64) + offsetof(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory));
+ }
+
+ // @todo: this is a bit awkward here, it would be better to make this assertion on
+ // PEDecoder instantiation. However, we don't necessarily have the NT headers there (in fact
+ // they might not exist.)
+
+ if (IsMapped())
+ {
+ // Ideally we would require the layout address to honor the section alignment constraints.
+ // However, we do have 8K aligned IL only images which we load on 32 bit platforms.
+ // Also in the case of files embedded within a single-file app, the default alignment for assemblies is 16 bytes.
+ CHECK(CheckAligned(m_base, 16));
+ }
+
+ // @todo: check NumberOfSections for overflow of SizeOfHeaders
+
+ UINT32 currentAddress = 0;
+ UINT32 currentOffset = 0;
+
+ CHECK(CheckSection(currentAddress, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders),
+ currentOffset, 0, VAL32(pNT->OptionalHeader.SizeOfHeaders)));
+
+ currentAddress=currentOffset=VAL32(pNT->OptionalHeader.SizeOfHeaders);
+
+ PTR_IMAGE_SECTION_HEADER section = FindFirstSection(pNT);
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections);
+
+ CHECK(sectionEnd >= section);
+
+
+ while (section < sectionEnd)
+ {
+
+ //
+ // NOTE: the if condition is becuase of a design issue in the CLR and OS loader's remapping
+ // of PE32 headers to PE32+. Because IMAGE_NT_HEADERS64 is bigger than IMAGE_NT_HEADERS32,
+ // the remapping will expand this part of the header and push out the following
+ // IMAGE_SECTION_HEADER entries. When IMAGE_DOS_HEADER::e_lfanew is large enough
+ // (size is proportional to the number of tools used to produce the inputs to the C++ linker)
+ // this can push the last section header
+ // beyond the boundary set by IMAGE_NT_HEADERS::OptionalHeader.SizeOfHeaders (e.g., this
+ // was recently seen where the unaligned size of the headers was 0x1f8 and SizeOfHeaders was
+ // 0x200, and the header remapping resulted in new headers size of 0x208). To compensate
+ // for this issue (it would be quite difficult to fix in the remapping code; see Dev11 430008)
+ // we assume that when the image is mapped that the needed validation has already been done.
+ //
+
+ if (!IsMapped())
+ {
+ CHECK(CheckBounds(dac_cast<PTR_CVOID>(pNT),VAL32(pNT->OptionalHeader.SizeOfHeaders),
+ section,sizeof(IMAGE_SECTION_HEADER)));
+ }
+
+ // Check flags
+ // Only allow a small list of characteristics
+ CHECK(!(section->Characteristics &
+ ~(VAL32((IMAGE_SCN_CNT_CODE |
+ IMAGE_SCN_CNT_INITIALIZED_DATA |
+ IMAGE_SCN_CNT_UNINITIALIZED_DATA|
+ IMAGE_SCN_MEM_DISCARDABLE |
+ IMAGE_SCN_MEM_NOT_CACHED |
+ IMAGE_SCN_MEM_NOT_PAGED |
+ IMAGE_SCN_MEM_EXECUTE |
+ IMAGE_SCN_MEM_READ |
+ IMAGE_SCN_MEM_WRITE |
+ // allow shared sections for all images for now.
+ // we'll constrain this in CheckILOnly
+ IMAGE_SCN_MEM_SHARED)))));
+
+ // we should not allow writable code sections, check if both flags are set
+ CHECK((section->Characteristics & VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE))) !=
+ VAL32((IMAGE_SCN_CNT_CODE|IMAGE_SCN_MEM_WRITE)));
+
+ CHECK(CheckSection(currentAddress, VAL32(section->VirtualAddress), VAL32(section->Misc.VirtualSize),
+ currentOffset, VAL32(section->PointerToRawData), VAL32(section->SizeOfRawData)));
+
+ currentAddress = VAL32(section->VirtualAddress)
+ + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(pNT->OptionalHeader.SectionAlignment));
+ currentOffset = VAL32(section->PointerToRawData) + VAL32(section->SizeOfRawData);
+
+ section++;
+ }
+
+ // Now check that the COR data directory is either NULL, or exists entirely in one section.
+ {
+ PTR_IMAGE_DATA_DIRECTORY pCORDataDir = pDataDirectories + IMAGE_DIRECTORY_ENTRY_COMHEADER;
+ CHECK(CheckRva(VAL32(pCORDataDir->VirtualAddress), VAL32(pCORDataDir->Size), 0, NULL_OK));
+ }
+
+ // @todo: verify directory entries
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_NT_CHECKED;
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckSection(COUNT_T previousAddressEnd, COUNT_T addressStart, COUNT_T addressSize,
+ COUNT_T previousOffsetEnd, COUNT_T offsetStart, COUNT_T offsetSize) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ // Fetch the NT header
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+
+ // OS will zero pad a mapped file up to file alignment size - some images rely on this
+ // COUNT_T alignedSize = AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment));
+ COUNT_T alignedSize = IsMapped() ? AlignUp(m_size, VAL32(pNT->OptionalHeader.FileAlignment)) : m_size;
+
+ // Check to make sure that our memory is big enough to cover the stated range.
+ // Note that this check is only required if we have a non-flat image.
+ if (IsMapped())
+ CHECK(alignedSize >= VAL32(pNT->OptionalHeader.SizeOfImage));
+
+ // Check expected alignments
+ CHECK(CheckAligned(addressStart, VAL32(pNT->OptionalHeader.SectionAlignment)));
+ CHECK(CheckAligned(offsetStart, VAL32(pNT->OptionalHeader.FileAlignment)));
+ CHECK(CheckAligned(offsetSize, VAL32(pNT->OptionalHeader.FileAlignment)));
+
+ // addressSize is typically not aligned, so we align it for purposes of checks.
+ COUNT_T alignedAddressSize = AlignUp(addressSize, VAL32(pNT->OptionalHeader.SectionAlignment));
+ CHECK(addressSize <= alignedAddressSize);
+
+ // Check overflow
+ CHECK(CheckOverflow(addressStart, alignedAddressSize));
+ CHECK(CheckOverflow(offsetStart, offsetSize));
+
+ // Make sure we don't overlap the previous section
+ CHECK(addressStart >= previousAddressEnd
+ && (offsetSize == 0
+ || offsetStart >= previousOffsetEnd));
+
+ // Make sure we don't overrun the end of the mapped image
+ CHECK(addressStart + alignedAddressSize <= VAL32(pNT->OptionalHeader.SizeOfImage));
+
+ // Make sure we don't overrun the end of the file (only relevant if we're not mapped, otherwise
+ // we don't know the file size, as it's not declared in the headers.)
+ if (!IsMapped())
+ CHECK(offsetStart + offsetSize <= alignedSize);
+
+ // Make sure the data doesn't overrun the virtual address space
+ CHECK(offsetSize <= alignedAddressSize);
+
+ CHECK_OK;
+}
+
+BOOL PEDecoder::HasWriteableSections() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckFormat());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ PTR_IMAGE_SECTION_HEADER pSection = FindFirstSection();
+ _ASSERTE(pSection != NULL);
+
+ PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (pSection < pSectionEnd)
+ {
+ if ((pSection->Characteristics & VAL32(IMAGE_SCN_MEM_WRITE)) != 0)
+ {
+ return TRUE;
+ }
+
+ pSection++;
+ }
+
+ return FALSE;
+}
+
+CHECK PEDecoder::CheckDirectoryEntry(int entry, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(entry < IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
+ PRECONDITION(HasDirectoryEntry(entry));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckDirectory(GetDirectoryEntry(entry), forbiddenFlags, ok));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckDirectory(IMAGE_DATA_DIRECTORY *pDir, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckPointer(pDir));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(CheckRva(VAL32(pDir->VirtualAddress), VAL32(pDir->Size), forbiddenFlags, ok));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckRva(RVA rva, COUNT_T size, int forbiddenFlags, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ if (rva == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ IMAGE_SECTION_HEADER *section = RvaToSection(rva);
+
+ CHECK(section != NULL);
+
+ CHECK(CheckBounds(VAL32(section->VirtualAddress),
+ // AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment)),
+ (UINT)VAL32(section->Misc.VirtualSize),
+ rva, size));
+ if(!IsMapped())
+ {
+ CHECK(CheckBounds(VAL32(section->VirtualAddress), VAL32(section->SizeOfRawData), rva, size));
+ }
+
+ if (forbiddenFlags!=0)
+ CHECK((section->Characteristics & VAL32(forbiddenFlags))==0);
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckRva(RVA rva, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ if (rva == 0)
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ else
+ CHECK(RvaToSection(rva) != NULL);
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (fileOffset == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "zero fileOffset illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ IMAGE_SECTION_HEADER *section = OffsetToSection(fileOffset);
+
+ CHECK(section != NULL);
+
+ CHECK(CheckBounds(section->PointerToRawData, section->SizeOfRawData,
+ fileOffset, size));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckOffset(COUNT_T fileOffset, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (fileOffset == NULL)
+ CHECK_MSG(ok == NULL_OK, "Null pointer illegal");
+ else
+ {
+ CHECK(OffsetToSection(fileOffset) != NULL);
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckData(const void *data, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (data == NULL)
+ {
+ CHECK_MSG(ok == NULL_OK, "NULL pointer illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ CHECK(CheckUnderflow(data, m_base));
+ CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX);
+
+ if (IsMapped())
+ CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size));
+ else
+ CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base), size));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckData(const void *data, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (data == NULL)
+ CHECK_MSG(ok == NULL_OK, "Null pointer illegal");
+ else
+ {
+ CHECK(CheckUnderflow(data, m_base));
+ CHECK((UINT_PTR) (((BYTE *) data) - ((BYTE *) m_base)) <= COUNT_T_MAX);
+
+ if (IsMapped())
+ CHECK(CheckRva((COUNT_T) ((BYTE *) data - (BYTE *) m_base)));
+ else
+ CHECK(CheckOffset((COUNT_T) ((BYTE *) data - (BYTE *) m_base)));
+ }
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckInternalAddress(SIZE_T address, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (address == 0)
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ else
+ CHECK(RvaToSection(InternalAddressToRva(address)) != NULL);
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckInternalAddress(SIZE_T address, COUNT_T size, IsNullOK ok) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (address == 0)
+ {
+ CHECK_MSG(ok == NULL_OK, "Zero RVA illegal");
+ CHECK(size == 0);
+ }
+ else
+ {
+ CHECK(CheckRva(InternalAddressToRva(address), size));
+ }
+
+ CHECK_OK;
+}
+
+RVA PEDecoder::InternalAddressToRva(SIZE_T address) const
+{
+ CONTRACT(RVA)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckRva(RETVAL));
+ }
+ CONTRACT_END;
+
+ if (m_flags & FLAG_RELOCATED)
+ {
+ // Address has been fixed up
+ RETURN (RVA) ((BYTE *) address - (BYTE *) m_base);
+ }
+ else
+ {
+ // Address has not been fixed up
+ RETURN (RVA) (address - (SIZE_T) GetPreferredBase());
+ }
+}
+
+// Returns a pointer to the named section or NULL if not found.
+// The name should include the starting "." as well.
+IMAGE_SECTION_HEADER *PEDecoder::FindSection(LPCSTR sectionName) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(sectionName != NULL);
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ }
+ CONTRACT_END;
+
+ // Ensure that the section name length is valid
+ SIZE_T iSectionNameLength = strlen(sectionName);
+ if ((iSectionNameLength < 1) || (iSectionNameLength > IMAGE_SIZEOF_SHORT_NAME))
+ {
+ _ASSERTE(!"Invalid section name!");
+ RETURN NULL;
+ }
+
+ // Get the start and ends of the sections
+ PTR_IMAGE_SECTION_HEADER pSection = FindFirstSection(FindNTHeaders());
+ _ASSERTE(pSection != NULL);
+ PTR_IMAGE_SECTION_HEADER pSectionEnd = pSection + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+ _ASSERTE(pSectionEnd != NULL);
+
+ BOOL fFoundSection = FALSE;
+
+ // Loop thru the sections and see if we got the section we are interested in
+ while (pSection < pSectionEnd)
+ {
+ // Is this the section we are looking for?
+ if (strncmp(sectionName, (char*)pSection->Name, iSectionNameLength) == 0)
+ {
+ // We found our section - break out of the loop
+ fFoundSection = TRUE;
+ break;
+ }
+
+ // Move to the next section
+ pSection++;
+ }
+
+ if (TRUE == fFoundSection)
+ RETURN pSection;
+ else
+ RETURN NULL;
+}
+
+IMAGE_SECTION_HEADER *PEDecoder::RvaToSection(RVA rva) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders()));
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (section < sectionEnd)
+ {
+ if (rva < (VAL32(section->VirtualAddress)
+ + AlignUp((UINT)VAL32(section->Misc.VirtualSize), (UINT)VAL32(FindNTHeaders()->OptionalHeader.SectionAlignment))))
+ {
+ if (rva < VAL32(section->VirtualAddress))
+ RETURN NULL;
+ else
+ {
+ RETURN section;
+ }
+ }
+
+ section++;
+ }
+
+ RETURN NULL;
+}
+
+IMAGE_SECTION_HEADER *PEDecoder::OffsetToSection(COUNT_T fileOffset) const
+{
+ CONTRACT(IMAGE_SECTION_HEADER *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ PTR_IMAGE_SECTION_HEADER section = dac_cast<PTR_IMAGE_SECTION_HEADER>(FindFirstSection(FindNTHeaders()));
+ PTR_IMAGE_SECTION_HEADER sectionEnd = section + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ while (section < sectionEnd)
+ {
+ if (fileOffset < section->PointerToRawData + section->SizeOfRawData)
+ {
+ if (fileOffset < section->PointerToRawData)
+ RETURN NULL;
+ else
+ RETURN section;
+ }
+
+ section++;
+ }
+
+ RETURN NULL;
+}
+
+TADDR PEDecoder::GetRvaData(RVA rva, IsNullOK ok /*= NULL_NOT_OK*/) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckRva(rva, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if ((rva == 0)&&(ok == NULL_NOT_OK))
+ RETURN NULL;
+
+ RVA offset;
+ if (IsMapped())
+ offset = rva;
+ else
+ {
+ // !!! check for case where rva is in padded portion of segment
+ offset = RvaToOffset(rva);
+ }
+
+ RETURN( m_base + offset );
+}
+
+RVA PEDecoder::GetDataRva(const TADDR data) const
+{
+ CONTRACT(RVA)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckData((void *)data, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (data == NULL)
+ RETURN 0;
+
+ COUNT_T offset = (COUNT_T) (data - m_base);
+ if (IsMapped())
+ RETURN offset;
+ else
+ RETURN OffsetToRva(offset);
+}
+
+BOOL PEDecoder::PointerInPE(PTR_CVOID data) const
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ TADDR taddrData = dac_cast<TADDR>(data);
+ TADDR taddrBase = dac_cast<TADDR>(m_base);
+
+ if (this->IsMapped())
+ {
+ return taddrBase <= taddrData && taddrData < taddrBase + GetVirtualSize();
+ }
+ else
+ {
+ return taddrBase <= taddrData && taddrData < taddrBase + GetSize();
+ }
+}
+
+TADDR PEDecoder::GetOffsetData(COUNT_T fileOffset, IsNullOK ok /*= NULL_NOT_OK*/) const
+{
+ CONTRACT(TADDR)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ PRECONDITION(CheckOffset(fileOffset, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if ((fileOffset == 0)&&(ok == NULL_NOT_OK))
+ RETURN NULL;
+
+ RETURN GetRvaData(OffsetToRva(fileOffset));
+}
+
+//-------------------------------------------------------------------------------
+// Lifted from "..\md\inc\mdfileformat.h"
+// (cannot #include it here because it references lot of other stuff)
+#define STORAGE_MAGIC_SIG 0x424A5342 // BSJB
+struct STORAGESIGNATURE
+{
+ ULONG lSignature; // "Magic" signature.
+ USHORT iMajorVer; // Major file version.
+ USHORT iMinorVer; // Minor file version.
+ ULONG iExtraData; // Offset to next structure of information
+ ULONG iVersionString; // Length of version string
+};
+typedef STORAGESIGNATURE UNALIGNED * PSTORAGESIGNATURE;
+typedef DPTR(STORAGESIGNATURE UNALIGNED) PTR_STORAGESIGNATURE;
+
+
+struct STORAGEHEADER
+{
+ BYTE fFlags; // STGHDR_xxx flags.
+ BYTE pad;
+ USHORT iStreams; // How many streams are there.
+};
+typedef STORAGEHEADER UNALIGNED * PSTORAGEHEADER;
+typedef DPTR(STORAGEHEADER UNALIGNED) PTR_STORAGEHEADER;
+
+
+struct STORAGESTREAM
+{
+ ULONG iOffset; // Offset in file for this stream.
+ ULONG iSize; // Size of the file.
+ char rcName[32]; // Start of name, null terminated.
+};
+typedef STORAGESTREAM UNALIGNED * PSTORAGESTREAM;
+typedef DPTR(STORAGESTREAM UNALIGNED) PTR_STORAGESTREAM;
+
+
+// if the stream's name is shorter than 32 bytes (incl.zero terminator),
+// the size of storage stream header is less than sizeof(STORAGESTREAM)
+// and is padded to 4-byte alignment
+inline PTR_STORAGESTREAM NextStorageStream(PTR_STORAGESTREAM pSS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC;
+ TADDR pc = dac_cast<TADDR>(pSS);
+ pc += (sizeof(STORAGESTREAM) - 32 /*sizeof(STORAGESTREAM::rcName)*/ + strlen(pSS->rcName)+1+3)&~3;
+ return PTR_STORAGESTREAM(pc);
+}
+//-------------------------------------------------------------------------------
+
+
+CHECK PEDecoder::CheckCorHeader() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+ if (m_flags & FLAG_COR_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckNTHeaders());
+
+ CHECK(HasCorHeader());
+
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_COMHEADER);
+
+ CHECK(CheckDirectory(pDir, IMAGE_SCN_MEM_WRITE, NULL_NOT_OK));
+
+ CHECK(VAL32(pDir->Size) >= sizeof(IMAGE_COR20_HEADER));
+
+ IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pDir->VirtualAddress));
+ CHECK(section != NULL);
+ CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0);
+
+ CHECK(CheckRva(VAL32(pDir->VirtualAddress), sizeof(IMAGE_COR20_HEADER)));
+
+ IMAGE_COR20_HEADER *pCor = GetCorHeader();
+
+ //CHECK(((ULONGLONG)pCor & 0x3)==0);
+
+ // If the file is COM+ 1.0, which by definition has nothing the runtime can
+ // use, or if the file requires a newer version of this engine than us,
+ // it cannot be run by this engine.
+ CHECK(VAL16(pCor->MajorRuntimeVersion) > 1 && VAL16(pCor->MajorRuntimeVersion) <= COR_VERSION_MAJOR);
+
+ CHECK(CheckDirectory(&pCor->MetaData, IMAGE_SCN_MEM_WRITE, HasNativeHeader() ? NULL_OK : NULL_NOT_OK));
+ CHECK(CheckDirectory(&pCor->Resources, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->StrongNameSignature, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->CodeManagerTable, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pCor->VTableFixups, 0, NULL_OK));
+ CHECK(CheckDirectory(&pCor->ExportAddressTableJumps, 0, NULL_OK));
+ CHECK(CheckDirectory(&pCor->ManagedNativeHeader, 0, NULL_OK));
+
+ CHECK(VAL32(pCor->cb) >= offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) + sizeof(IMAGE_DATA_DIRECTORY));
+
+ DWORD validBits = COMIMAGE_FLAGS_ILONLY
+ | COMIMAGE_FLAGS_32BITREQUIRED
+ | COMIMAGE_FLAGS_TRACKDEBUGDATA
+ | COMIMAGE_FLAGS_STRONGNAMESIGNED
+ | COMIMAGE_FLAGS_NATIVE_ENTRYPOINT
+ | COMIMAGE_FLAGS_IL_LIBRARY
+ | COMIMAGE_FLAGS_32BITPREFERRED;
+
+ CHECK((pCor->Flags&VAL32(~validBits)) == 0);
+
+ // Pure IL images should not have VTable fixups or EAT jumps
+ if (IsILOnly())
+ {
+ CHECK(pCor->VTableFixups.Size == VAL32(0));
+ CHECK(pCor->ExportAddressTableJumps.Size == VAL32(0));
+ CHECK(!(pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)));
+ //@TODO: If not an exe, check that EntryPointToken is mdNil
+ }
+ else
+ {
+ if (pCor->Flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT))
+ {
+ CHECK(CheckRva(VAL32(IMAGE_COR20_HEADER_FIELD(*pCor,EntryPointToken))));
+ }
+ }
+
+ // Strong name signed images should have a signature
+ if (IsStrongNameSigned())
+ CHECK(HasStrongNameSignature());
+
+ // IL library files (really a misnomer - these are native images or ReadyToRun images)
+ // only they can have a native image header
+ if ((pCor->Flags&VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) == 0)
+ {
+ CHECK(VAL32(pCor->ManagedNativeHeader.Size) == 0);
+ }
+
+ // Metadata header checks
+ IMAGE_DATA_DIRECTORY *pDirMD = &pCor->MetaData;
+ COUNT_T ctMD = (COUNT_T)VAL32(pDirMD->Size);
+ TADDR pcMD = (TADDR)GetDirectoryData(pDirMD);
+
+ if(pcMD != NULL)
+ {
+ // Storage signature checks
+ CHECK(ctMD >= sizeof(STORAGESIGNATURE));
+ PTR_STORAGESIGNATURE pStorageSig = PTR_STORAGESIGNATURE((TADDR)pcMD);
+ COUNT_T ctMDStreamSize = ctMD; // Store MetaData stream size for later usage
+
+
+ CHECK(VAL32(pStorageSig->lSignature) == STORAGE_MAGIC_SIG);
+ COUNT_T ctSSig;
+ CHECK(ClrSafeInt<COUNT_T>::addition(sizeof(STORAGESIGNATURE), (COUNT_T)VAL32(pStorageSig->iVersionString), ctSSig));
+ CHECK(ctMD > ctSSig);
+
+ // Storage header checks
+ pcMD += ctSSig;
+
+ PTR_STORAGEHEADER pSHdr = PTR_STORAGEHEADER((TADDR)pcMD);
+
+
+ ctMD -= ctSSig;
+ CHECK(ctMD >= sizeof(STORAGEHEADER));
+ pcMD = dac_cast<TADDR>(pSHdr) + sizeof(STORAGEHEADER);
+ ctMD -= sizeof(STORAGEHEADER);
+ WORD nStreams = VAL16(pSHdr->iStreams);
+
+ // Storage streams checks (pcMD is a target pointer, so watch out)
+ PTR_STORAGESTREAM pStr = PTR_STORAGESTREAM((TADDR)pcMD);
+ PTR_STORAGESTREAM pSSOutOfRange =
+ PTR_STORAGESTREAM((TADDR)(pcMD + ctMD));
+ size_t namelen;
+ WORD iStr;
+ PTR_STORAGESTREAM pSS;
+ for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++)
+ {
+ CHECK(pSS < pSSOutOfRange);
+ CHECK(pSS + 1 <= pSSOutOfRange);
+
+ for(namelen=0; (namelen<32)&&(pSS->rcName[namelen]!=0); namelen++);
+ CHECK((0 < namelen)&&(namelen < 32));
+
+ // Is it ngen image?
+ if (!HasNativeHeader())
+ {
+ // Forbid HOT_MODEL_STREAM for non-ngen images
+ CHECK(strcmp(pSS->rcName, HOT_MODEL_STREAM_A) != 0);
+ }
+
+ pcMD = dac_cast<TADDR>(NextStorageStream(pSS));
+ ctMD -= (COUNT_T)(pcMD - dac_cast<TADDR>(pSS));
+
+ pSS = PTR_STORAGESTREAM((TADDR)pcMD);
+ }
+
+ // At this moment, pcMD is pointing past the last stream header
+ // and ctMD contains total size left for streams per se
+ // Now, check the offsets and sizes of streams
+ COUNT_T ctStreamsBegin = (COUNT_T)(pcMD - dac_cast<TADDR>(pStorageSig)); // min.possible offset
+ COUNT_T ctSS, ctSSbegin, ctSSend = 0;
+ for(iStr = 1, pSS = pStr; iStr <= nStreams; iStr++,pSS = NextStorageStream(pSS))
+ {
+ ctSSbegin = (COUNT_T)VAL32(pSS->iOffset);
+ CHECK(ctStreamsBegin <= ctSSbegin);
+ CHECK(ctSSbegin < ctMDStreamSize);
+
+ ctSS = (COUNT_T)VAL32(pSS->iSize);
+ CHECK(ctMD >= ctSS);
+ CHECK(ClrSafeInt<COUNT_T>::addition(ctSSbegin, ctSS, ctSSend));
+ CHECK(ctSSend <= ctMDStreamSize);
+ ctMD -= ctSS;
+
+ // Check stream overlap
+ PTR_STORAGESTREAM pSSprior;
+ for(pSSprior=pStr; pSSprior < pSS; pSSprior = NextStorageStream(pSSprior))
+ {
+ COUNT_T ctSSprior_end = 0;
+ CHECK(ClrSafeInt<COUNT_T>::addition((COUNT_T)VAL32(pSSprior->iOffset), (COUNT_T)VAL32(pSSprior->iSize), ctSSprior_end));
+ CHECK((ctSSbegin >= ctSSprior_end)||(ctSSend <= (COUNT_T)VAL32(pSSprior->iOffset)));
+ }
+ }
+ } //end if(pcMD != NULL)
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_COR_CHECKED;
+
+ CHECK_OK;
+}
+
+
+
+// This function exists to provide compatibility between two different native image
+// (NGEN) formats. In particular, the manifest metadata blob and the full metadata
+// blob swapped locations from 3.5RTM to 3.5SP1. The logic here is to look at the
+// runtime version embedded in the native image, to determine which format it is.
+IMAGE_DATA_DIRECTORY *PEDecoder::GetMetaDataHelper(METADATA_SECTION_TYPE type) const
+{
+ CONTRACT(IMAGE_DATA_DIRECTORY *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+#ifdef FEATURE_PREJIT
+ PRECONDITION(type == METADATA_SECTION_FULL || type == METADATA_SECTION_MANIFEST);
+ PRECONDITION(type != METADATA_SECTION_MANIFEST || HasNativeHeader());
+#else // FEATURE_PREJIT
+ PRECONDITION(type == METADATA_SECTION_FULL);
+#endif // FEATURE_PREJIT
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDirRet = &GetCorHeader()->MetaData;
+
+#ifdef FEATURE_PREJIT
+ // For backward compatibility reasons, we must be able to locate the metadata in all v2 native images as
+ // well as current version of native images. This is needed by mdbg.exe for SxS debugging scenarios.
+ // Specifically, mdbg.exe can be used to debug v2 managed applications, and need to be able to find
+ // metadata in v2 native images. Therefore, the location of the data we need to locate the metadata must
+ // never be moved. Here are some asserts to ensure that.
+ // IMAGE_COR20_HEADER should be stable since it is defined in ECMA. Verify a coupld of fields we use:
+ _ASSERTE(offsetof(IMAGE_COR20_HEADER, MetaData) == 8);
+ _ASSERTE(offsetof(IMAGE_COR20_HEADER, ManagedNativeHeader) == 64);
+ // We use a couple of fields in CORCOMPILE_HEADER.
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, VersionInfo) == 40);
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, ManifestMetaData) == 88);
+ // And we use four version fields in CORCOMPILE_VERSION_INFO.
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMajor) == 4);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionMinor) == 6);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionBuildNumber) == 8);
+ _ASSERTE(offsetof(CORCOMPILE_VERSION_INFO, wVersionPrivateBuildNumber) == 10);
+
+ // Visual Studio took dependency on crossgen /CreatePDB returning COR_E_NI_AND_RUNTIME_VERSION_MISMATCH
+ // when crossgen and the native image come from different runtimes. In order to reach error path that returns
+ // COR_E_NI_AND_RUNTIME_VERSION_MISMATCH in this case, size of CORCOMPILE_HEADER has to remain constant,
+ // and the offset of PEKind and Machine fields inside CORCOMPILE_HEADER also have to remain constant, to pass earlier
+ // checks that lead to different error codes. See Windows Phone Blue Bug #45406 for details.
+ _ASSERTE(sizeof(CORCOMPILE_HEADER) == 160 + sizeof(TADDR));
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, PEKind) == 108 + sizeof(TADDR));
+ _ASSERTE(offsetof(CORCOMPILE_HEADER, Machine) == 116 + sizeof(TADDR));
+
+ // Handle NGEN format; otherwise, there is only one MetaData section in the
+ // COR_HEADER and so the value of pDirRet is correct
+ if (HasNativeHeader())
+ {
+
+ if (type == METADATA_SECTION_MANIFEST)
+ pDirRet = &GetNativeHeader()->ManifestMetaData;
+
+
+ }
+
+#endif // FEATURE_PREJIT
+
+ RETURN pDirRet;
+}
+
+PTR_CVOID PEDecoder::GetMetadata(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = GetMetaDataHelper(METADATA_SECTION_FULL);
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir));
+}
+
+const void *PEDecoder::GetResources(COUNT_T *pSize) const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (void *)GetDirectoryData(pDir);
+}
+
+CHECK PEDecoder::CheckResource(COUNT_T offset) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ }
+ CONTRACT_CHECK_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ CHECK(CheckOverflow(VAL32(pDir->VirtualAddress), offset));
+
+ RVA rva = VAL32(pDir->VirtualAddress) + offset;
+
+ // Make sure we have at least enough data for a length
+ CHECK(CheckRva(rva, sizeof(DWORD)));
+
+ // Make sure resource is within resource section
+ CHECK(CheckBounds(VAL32(pDir->VirtualAddress), VAL32(pDir->Size),
+ rva + sizeof(DWORD), GET_UNALIGNED_VAL32((LPVOID)GetRvaData(rva))));
+
+ CHECK_OK;
+}
+
+const void *PEDecoder::GetResource(COUNT_T offset, COUNT_T *pSize) const
+{
+ CONTRACT(const void *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->Resources;
+
+ // 403571: Prefix complained correctly about need to always perform rva check
+ if (CheckResource(offset) == FALSE)
+ return NULL;
+
+ void * resourceBlob = (void *)GetRvaData(VAL32(pDir->VirtualAddress) + offset);
+ // Holds if CheckResource(offset) == TRUE
+ PREFIX_ASSUME(resourceBlob != NULL);
+
+ if (pSize != NULL)
+ *pSize = GET_UNALIGNED_VAL32(resourceBlob);
+
+ RETURN (const void *) ((BYTE*)resourceBlob+sizeof(DWORD));
+}
+
+BOOL PEDecoder::HasManagedEntryPoint() const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ ULONG flags = GetCorHeader()->Flags;
+ return (!(flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) &&
+ (!IsNilToken(GetEntryPointToken())));
+}
+
+ULONG PEDecoder::GetEntryPointToken() const
+{
+ CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ RETURN VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken));
+}
+
+IMAGE_COR_VTABLEFIXUP *PEDecoder::GetVTableFixups(COUNT_T *pCount) const
+{
+ CONTRACT(IMAGE_COR_VTABLEFIXUP *)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckCorHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->VTableFixups;
+
+ if (pCount != NULL)
+ *pCount = VAL32(pDir->Size)/sizeof(IMAGE_COR_VTABLEFIXUP);
+
+ RETURN PTR_IMAGE_COR_VTABLEFIXUP(GetDirectoryData(pDir));
+}
+
+CHECK PEDecoder::CheckILOnly() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (m_flags & FLAG_IL_ONLY_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckCorHeader());
+
+ if (HasReadyToRunHeader())
+ {
+ // Pretend R2R images are IL-only
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_IL_ONLY_CHECKED;
+ CHECK_OK;
+ }
+
+ // Allow only verifiable directories.
+
+ static int s_allowedBitmap =
+ ((1 << (IMAGE_DIRECTORY_ENTRY_IMPORT )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_RESOURCE )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_SECURITY )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_BASERELOC)) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_DEBUG )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_IAT )) |
+ (1 << (IMAGE_DIRECTORY_ENTRY_COMHEADER)));
+
+
+
+
+ for (UINT32 entry=0; entry<GetNumberOfRvaAndSizes(); ++entry)
+ {
+ if (Has32BitNTHeaders())
+ CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders32()->OptionalHeader),
+ GetNTHeaders32()->FileHeader.SizeOfOptionalHeader,
+ dac_cast<PTR_CVOID>(GetNTHeaders32()->OptionalHeader.DataDirectory + entry),
+ sizeof(IMAGE_DATA_DIRECTORY));
+ else
+ CheckBounds(dac_cast<PTR_CVOID>(&GetNTHeaders64()->OptionalHeader),
+ GetNTHeaders32()->FileHeader.SizeOfOptionalHeader,
+ dac_cast<PTR_CVOID>(GetNTHeaders64()->OptionalHeader.DataDirectory + entry),
+ sizeof(IMAGE_DATA_DIRECTORY));
+
+ if (HasDirectoryEntry(entry))
+ {
+ CHECK((s_allowedBitmap & (1 << entry)) != 0);
+ if (entry!=IMAGE_DIRECTORY_ENTRY_SECURITY) //ignored by OS loader
+ CHECK(CheckDirectoryEntry(entry,IMAGE_SCN_MEM_SHARED,NULL_NOT_OK));
+ }
+ }
+ if (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) ||
+ HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC) ||
+ FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0)
+ {
+ // When the image is LoadLibrary'd, we whack the import, IAT directories and the entrypoint. We have to relax
+ // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image.
+ if (!IsMapped() || (HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT) || HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC)))
+ {
+ CHECK(CheckILOnlyImportDlls());
+ CHECK(CheckILOnlyBaseRelocations());
+ }
+
+#ifdef TARGET_X86
+ if (!IsMapped())
+ {
+ CHECK(CheckILOnlyEntryPoint());
+ }
+#endif
+ }
+
+ // Check some section characteristics
+ IMAGE_NT_HEADERS *pNT = FindNTHeaders();
+ IMAGE_SECTION_HEADER *section = FindFirstSection(pNT);
+ IMAGE_SECTION_HEADER *sectionEnd = section + VAL16(pNT->FileHeader.NumberOfSections);
+ while (section < sectionEnd)
+ {
+ // Don't allow shared sections for IL-only images
+ CHECK(!(section->Characteristics & IMAGE_SCN_MEM_SHARED));
+
+ // Be sure that we have some access to the section. Note that this test assumes that
+ // execute or write permissions will also let us read the section. If that is found to be an
+ // incorrect assumption, this will need to be modified.
+ CHECK((section->Characteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) != 0);
+ section++;
+ }
+
+ // For EXE, check that OptionalHeader.Win32VersionValue is zero. When this value is non-zero, GetVersionEx
+ // returns PE supplied values, rather than native OS values; the runtime relies on knowing the actual
+ // OS version.
+ if (!IsDll())
+ {
+ CHECK(GetWin32VersionValue() == 0);
+ }
+
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_IL_ONLY_CHECKED;
+
+ CHECK_OK;
+}
+
+
+CHECK PEDecoder::CheckILOnlyImportDlls() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ // The only allowed DLL Imports are MscorEE.dll:_CorExeMain,_CorDllMain
+
+#ifdef HOST_64BIT
+ // On win64, when the image is LoadLibrary'd, we whack the import and IAT directories. We have to relax
+ // the verification for mapped images. Ideally, we would only do it for a post-LoadLibrary image.
+ if (IsMapped() && !HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT))
+ CHECK_OK;
+#endif
+
+ CHECK(HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT));
+ CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_SCN_MEM_WRITE));
+
+ // Get the import directory entry
+ PIMAGE_DATA_DIRECTORY pDirEntryImport = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_IMPORT);
+ CHECK(pDirEntryImport != NULL);
+ PREFIX_ASSUME(pDirEntryImport != NULL);
+
+ // There should be space for 2 entries. (mscoree and NULL)
+ CHECK(VAL32(pDirEntryImport->Size) >= (2 * sizeof(IMAGE_IMPORT_DESCRIPTOR)));
+
+ // Get the import data
+ PIMAGE_IMPORT_DESCRIPTOR pID = (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryData(pDirEntryImport);
+ CHECK(pID != NULL);
+ PREFIX_ASSUME(pID != NULL);
+
+ // Entry 0: ILT, Name, IAT must be be non-null. Forwarder, DateTime should be NULL.
+ CHECK( IMAGE_IMPORT_DESC_FIELD(pID[0], Characteristics) != 0
+ && pID[0].TimeDateStamp == 0
+ && (pID[0].ForwarderChain == 0 || pID[0].ForwarderChain == static_cast<ULONG>(-1))
+ && pID[0].Name != 0
+ && pID[0].FirstThunk != 0);
+
+ // Entry 1: must be all nulls.
+ CHECK( IMAGE_IMPORT_DESC_FIELD(pID[1], Characteristics) == 0
+ && pID[1].TimeDateStamp == 0
+ && pID[1].ForwarderChain == 0
+ && pID[1].Name == 0
+ && pID[1].FirstThunk == 0);
+
+ // Ensure the RVA of the name plus its length is valid for this image
+ UINT nameRVA = VAL32(pID[0].Name);
+ CHECK(CheckRva(nameRVA, (COUNT_T) sizeof("mscoree.dll")));
+
+ // Make sure the name is equal to mscoree
+ CHECK(SString::_stricmp( (char *)GetRvaData(nameRVA), "mscoree.dll") == 0);
+
+ // Check the Hint/Name table.
+ CHECK(CheckILOnlyImportByNameTable(VAL32(IMAGE_IMPORT_DESC_FIELD(pID[0], OriginalFirstThunk))));
+
+ // The IAT needs to be checked only for size.
+ CHECK(CheckRva(VAL32(pID[0].FirstThunk), 2*sizeof(UINT32)));
+
+ CHECK_OK;
+}
+
+CHECK PEDecoder::CheckILOnlyImportByNameTable(RVA rva) const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ // Check if we have enough space to hold 2 DWORDS
+ CHECK(CheckRva(rva, 2*sizeof(UINT32)));
+
+ UINT32 UNALIGNED *pImportArray = (UINT32 UNALIGNED *) GetRvaData(rva);
+
+ CHECK(GET_UNALIGNED_VAL32(&pImportArray[0]) != 0);
+ CHECK(GET_UNALIGNED_VAL32(&pImportArray[1]) == 0);
+
+ UINT32 importRVA = GET_UNALIGNED_VAL32(&pImportArray[0]);
+
+ // First bit Set implies Ordinal lookup
+ CHECK((importRVA & 0x80000000) == 0);
+
+#define DLL_NAME "_CorDllMain"
+#define EXE_NAME "_CorExeMain"
+
+ static_assert_no_msg(sizeof(DLL_NAME) == sizeof(EXE_NAME));
+
+ // Check if we have enough space to hold 2 bytes +
+ // _CorExeMain or _CorDllMain and a NULL char
+ CHECK(CheckRva(importRVA, offsetof(IMAGE_IMPORT_BY_NAME, Name) + sizeof(DLL_NAME)));
+
+ IMAGE_IMPORT_BY_NAME *import = (IMAGE_IMPORT_BY_NAME*) GetRvaData(importRVA);
+
+ CHECK(SString::_stricmp((char *) import->Name, DLL_NAME) == 0 || _stricmp((char *) import->Name, EXE_NAME) == 0);
+
+ CHECK_OK;
+}
+
+#ifdef TARGET_X86
+// jmp dword ptr ds:[XXXX]
+#define JMP_DWORD_PTR_DS_OPCODE { 0xFF, 0x25 }
+#define JMP_DWORD_PTR_DS_OPCODE_SIZE 2 // Size of opcode
+#define JMP_SIZE 6 // Size of opcode + operand
+#endif
+
+CHECK PEDecoder::CheckILOnlyBaseRelocations() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC))
+ {
+ // We require base relocs for dlls.
+ CHECK(!IsDll());
+
+ CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) != 0);
+ }
+ else
+ {
+ CHECK((FindNTHeaders()->FileHeader.Characteristics & VAL16(IMAGE_FILE_RELOCS_STRIPPED)) == 0);
+
+ CHECK(CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_SCN_MEM_WRITE));
+
+ IMAGE_DATA_DIRECTORY *pRelocDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+
+ IMAGE_SECTION_HEADER *section = RvaToSection(VAL32(pRelocDir->VirtualAddress));
+ CHECK(section != NULL);
+ CHECK((section->Characteristics & VAL32(IMAGE_SCN_MEM_READ))!=0);
+
+ IMAGE_BASE_RELOCATION *pReloc = (IMAGE_BASE_RELOCATION *)
+ GetRvaData(VAL32(pRelocDir->VirtualAddress));
+
+ // 403569: PREfix correctly complained about pReloc being possibly NULL
+ CHECK(pReloc != NULL);
+ CHECK(VAL32(pReloc->SizeOfBlock) == VAL32(pRelocDir->Size));
+
+ UINT16 *pRelocEntry = (UINT16 *) (pReloc + 1);
+ UINT16 *pRelocEntryEnd = (UINT16 *) ((BYTE *) pReloc + VAL32(pReloc->SizeOfBlock));
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_IA64))
+ {
+ // Exactly 2 Reloc records, both IMAGE_REL_BASED_DIR64
+ CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+2*sizeof(UINT16)));
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ pRelocEntry++;
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ }
+ else
+ {
+ // Only one Reloc record is expected
+ CHECK(VAL32(pReloc->SizeOfBlock) >= (sizeof(IMAGE_BASE_RELOCATION)+sizeof(UINT16)));
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_AMD64))
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_DIR64 << 12));
+ else
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == (IMAGE_REL_BASED_HIGHLOW << 12));
+ }
+
+ while (++pRelocEntry < pRelocEntryEnd)
+ {
+ // NULL padding entries are allowed
+ CHECK((VAL16(pRelocEntry[0]) & 0xF000) == IMAGE_REL_BASED_ABSOLUTE);
+ }
+ }
+
+ CHECK_OK;
+}
+
+#ifdef TARGET_X86
+CHECK PEDecoder::CheckILOnlyEntryPoint() const
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ CHECK(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint != 0);
+
+ if(FindNTHeaders()->FileHeader.Machine == VAL16(IMAGE_FILE_MACHINE_I386))
+ {
+ // EntryPoint should be a jmp dword ptr ds:[XXXX] instruction.
+ // XXXX should be RVA of the first and only entry in the IAT.
+
+ CHECK(CheckRva(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint), JMP_SIZE));
+
+ BYTE *stub = (BYTE *) GetRvaData(VAL32(FindNTHeaders()->OptionalHeader.AddressOfEntryPoint));
+
+ static const BYTE s_DllOrExeMain[] = JMP_DWORD_PTR_DS_OPCODE;
+
+ // 403570: prefix complained about stub being possibly NULL.
+ // Unsure here. PREFIX_ASSUME might be also correct as indices are
+ // verified in the above CHECK statement.
+ CHECK(stub != NULL);
+ CHECK(memcmp(stub, s_DllOrExeMain, JMP_DWORD_PTR_DS_OPCODE_SIZE) == 0);
+
+ // Verify target of jump - it should be first entry in the IAT.
+
+ PIMAGE_IMPORT_DESCRIPTOR pID =
+ (PIMAGE_IMPORT_DESCRIPTOR) GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_IMPORT);
+
+ UINT32 va = * (UINT32 *) (stub + JMP_DWORD_PTR_DS_OPCODE_SIZE);
+
+ CHECK(VAL32(pID[0].FirstThunk) == (va - (SIZE_T) GetPreferredBase()));
+ }
+
+ CHECK_OK;
+}
+#endif // TARGET_X86
+
+#ifndef DACCESS_COMPILE
+
+void PEDecoder::LayoutILOnly(void *base, BOOL allowFullPE) const
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(allowFullPE || CheckILOnlyFormat());
+ PRECONDITION(CheckZeroedMemory(base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfImage)));
+ // Ideally we would require the layout address to honor the section alignment constraints.
+ // However, we do have 8K aligned IL only images which we load on 32 bit platforms. In this
+ // case, we can only guarantee OS page alignment (which after all, is good enough.)
+ PRECONDITION(CheckAligned((SIZE_T)base, GetOsPageSize()));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // We're going to copy everything first, and write protect what we need to later.
+
+ // First, copy headers
+ CopyMemory(base, (void *)m_base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders));
+
+ // Now, copy all sections to appropriate virtual address
+
+ IMAGE_SECTION_HEADER *sectionStart = IMAGE_FIRST_SECTION(FindNTHeaders());
+ IMAGE_SECTION_HEADER *sectionEnd = sectionStart + VAL16(FindNTHeaders()->FileHeader.NumberOfSections);
+
+ IMAGE_SECTION_HEADER *section = sectionStart;
+ while (section < sectionEnd)
+ {
+ // Raw data may be less than section size if tail is zero, but may be more since VirtualSize is
+ // not padded.
+ DWORD size = min(VAL32(section->SizeOfRawData), VAL32(section->Misc.VirtualSize));
+
+ CopyMemory((BYTE *) base + VAL32(section->VirtualAddress), (BYTE *) m_base + VAL32(section->PointerToRawData), size);
+
+ // Note that our memory is zeroed already, so no need to initialize any tail.
+
+ section++;
+ }
+
+ // Apply write protection to copied headers
+ DWORD oldProtection;
+ if (!ClrVirtualProtect((void *) base, VAL32(FindNTHeaders()->OptionalHeader.SizeOfHeaders),
+ PAGE_READONLY, &oldProtection))
+ ThrowLastError();
+
+ // Finally, apply proper protection to copied sections
+ for (section = sectionStart; section < sectionEnd; section++)
+ {
+ // Add appropriate page protection.
+#if defined(CROSSGEN_COMPILE) || defined(TARGET_UNIX)
+ if (section->Characteristics & IMAGE_SCN_MEM_WRITE)
+ continue;
+
+ DWORD newProtection = PAGE_READONLY;
+#else
+ DWORD newProtection = section->Characteristics & IMAGE_SCN_MEM_EXECUTE ?
+ PAGE_EXECUTE_READ :
+ section->Characteristics & IMAGE_SCN_MEM_WRITE ?
+ PAGE_READWRITE :
+ PAGE_READONLY;
+#endif
+
+ if (!ClrVirtualProtect((void*)((BYTE*)base + VAL32(section->VirtualAddress)),
+ VAL32(section->Misc.VirtualSize),
+ newProtection, &oldProtection))
+ {
+ ThrowLastError();
+ }
+ }
+
+ RETURN;
+}
+
+#endif // #ifndef DACCESS_COMPILE
+
+bool ReadResourceDirectoryHeader(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, IMAGE_RESOURCE_DIRECTORY_ENTRY** ppDirectoryEntries, IMAGE_RESOURCE_DIRECTORY **ppResourceDirectory)
+{
+ if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DIRECTORY)))
+ {
+ return false;
+ }
+
+ *ppResourceDirectory = (IMAGE_RESOURCE_DIRECTORY *)pDecoder->GetRvaData(rva);
+
+ // Check to see if entire resource directory is accessible
+ if (!pDecoder->CheckRva(rva + sizeof(IMAGE_RESOURCE_DIRECTORY),
+ (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfNamedEntries) +
+ (sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY) * (*ppResourceDirectory)->NumberOfIdEntries)))
+ {
+ return false;
+ }
+
+ *ppDirectoryEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)pDecoder->GetRvaData(rva + sizeof(IMAGE_RESOURCE_DIRECTORY));
+ return true;
+}
+
+bool ReadNameFromResourceDirectoryEntry(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries, DWORD iEntry, DWORD *pNameUInt, WCHAR **pNameStr)
+{
+ *pNameStr = NULL;
+ *pNameUInt = 0;
+
+ if (!IS_INTRESOURCE(pDirectoryEntries[iEntry].Name))
+ {
+ DWORD entryName = pDirectoryEntries[iEntry].Name;
+ if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING))
+ return false;
+ DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection;
+
+ if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD)))
+ return false;
+
+ size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva);
+ if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen))))
+ return false;
+ *pNameStr = new(nothrow) WCHAR[entryNameLen + 1];
+ if ((*pNameStr) == NULL)
+ return false;
+ memcpy((*pNameStr), (WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), entryNameLen * sizeof(WCHAR));
+ (*pNameStr)[entryNameLen] = 0;
+ }
+ else
+ {
+ DWORD name = pDirectoryEntries[iEntry].Name;
+ if (!IS_INTRESOURCE(name))
+ return false;
+
+ *pNameUInt = name;
+ }
+
+ return true;
+}
+
+DWORD ReadResourceDirectory(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rva, LPCWSTR name, BOOL *pisDirectory)
+{
+ *pisDirectory = FALSE;
+
+ IMAGE_RESOURCE_DIRECTORY* pResourceDirectory;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries;
+ if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rva, &pDirectoryEntries, &pResourceDirectory))
+ {
+ return 0;
+ }
+
+ // A fast implementation of resource lookup uses a binary search, but our needs are simple, and a linear search
+ // is easier to prove correct, so do that instead.
+ DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries;
+
+ for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++)
+ {
+ BOOL foundEntry = FALSE;
+
+ if (IS_INTRESOURCE(name))
+ {
+ // name is id
+ if (pDirectoryEntries[iEntry].Name == (DWORD)(SIZE_T)name)
+ foundEntry = TRUE;
+ }
+ else
+ {
+ // name is string
+ DWORD entryName = pDirectoryEntries[iEntry].Name;
+ if (!(entryName & IMAGE_RESOURCE_NAME_IS_STRING))
+ continue;
+
+ DWORD entryNameRva = (entryName & ~IMAGE_RESOURCE_NAME_IS_STRING) + rvaOfResourceSection;
+
+ if (!pDecoder->CheckRva(entryNameRva, sizeof(WORD)))
+ return 0;
+
+ size_t entryNameLen = *(WORD*)pDecoder->GetRvaData(entryNameRva);
+ if (wcslen(name) != entryNameLen)
+ continue;
+
+ if (!pDecoder->CheckRva(entryNameRva, (COUNT_T)(sizeof(WORD) * (1 + entryNameLen))))
+ return 0;
+
+ if (memcmp((WCHAR*)pDecoder->GetRvaData(entryNameRva + sizeof(WORD)), name, entryNameLen * sizeof(WCHAR)) == 0)
+ foundEntry = TRUE;
+ }
+
+ if (!foundEntry)
+ continue;
+
+ *pisDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
+ DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
+ DWORD dataRva = rvaOfResourceSection + offsetToData;
+ return dataRva;
+ }
+
+ return 0;
+}
+
+DWORD ReadResourceDataEntry(const PEDecoder *pDecoder, DWORD rva, COUNT_T *pSize)
+{
+ *pSize = 0;
+
+ if (!pDecoder->CheckRva(rva, sizeof(IMAGE_RESOURCE_DATA_ENTRY)))
+ {
+ return 0;
+ }
+
+ IMAGE_RESOURCE_DATA_ENTRY *pDataEntry = (IMAGE_RESOURCE_DATA_ENTRY *)pDecoder->GetRvaData(rva);
+ *pSize = pDataEntry->Size;
+ return pDataEntry->OffsetToData;
+}
+
+void * PEDecoder::GetWin32Resource(LPCWSTR lpName, LPCWSTR lpType, COUNT_T *pSize /*=NULL*/) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(IsMapped());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ COUNT_T sizeUnused = 0; // Use this variable if pSize is null
+ if (pSize == NULL)
+ pSize = &sizeUnused;
+
+ *pSize = 0;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return NULL;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return NULL;
+
+ BOOL isDirectory = FALSE;
+ DWORD nameTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, pDir->VirtualAddress, lpType, &isDirectory);
+
+ if (!isDirectory)
+ return NULL;
+
+ if (nameTableRva == 0)
+ return NULL;
+
+ DWORD languageTableRva = ReadResourceDirectory(this, pDir->VirtualAddress, nameTableRva, lpName, &isDirectory);
+ if (!isDirectory)
+ return NULL;
+
+ if (languageTableRva == 0)
+ return NULL;
+
+ // This api is designed to find resources with LANGID = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)
+ // This translates to LANGID 0 as the initial lookup point, which is sufficient for the needs of this api at this time
+ // (FindResource in the Windows api implements a large number of fallback paths which this api does not implement)
+
+ DWORD resourceDataEntryRva = ReadResourceDirectory(this, pDir->VirtualAddress, languageTableRva, 0, &isDirectory);
+ if (isDirectory) // This must not be a resource directory itself
+ return NULL;
+
+ if (resourceDataEntryRva == 0)
+ return NULL;
+
+ DWORD resourceDataRva = ReadResourceDataEntry(this, resourceDataEntryRva, pSize);
+ if (!CheckRva(resourceDataRva, *pSize))
+ {
+ *pSize = 0;
+ return NULL;
+ }
+
+ return (void*)GetRvaData(resourceDataRva);
+}
+
+typedef bool (*PEDecoder_EnumerateResourceTableFunction)(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context);
+
+struct ResourceEnumerateNamesState
+{
+ PEDecoder_ResourceNamesCallbackFunction namesCallback;
+ PEDecoder_ResourceCallbackFunction langIDcallback;
+ void *context;
+ LPCWSTR nameType;
+ LPCWSTR nameName;
+ PEDecoder_EnumerateResourceTableFunction callbackPerName;
+ PEDecoder_EnumerateResourceTableFunction callbackPerLangID;
+};
+
+struct ResourceEnumerateTypesState
+{
+ PEDecoder_ResourceTypesCallbackFunction callback;
+ void *context;
+ LPCWSTR nameType;
+};
+
+bool EnumerateWin32ResourceTable(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, DWORD rvaOfResourceTable, PEDecoder_EnumerateResourceTableFunction resourceTableEnumerator, void *context)
+{
+ IMAGE_RESOURCE_DIRECTORY* pResourceDirectory;
+ IMAGE_RESOURCE_DIRECTORY_ENTRY* pDirectoryEntries;
+ if (!ReadResourceDirectoryHeader(pDecoder, rvaOfResourceSection, rvaOfResourceTable, &pDirectoryEntries, &pResourceDirectory))
+ {
+ return false;
+ }
+
+ DWORD iEntryCount = (DWORD)pResourceDirectory->NumberOfNamedEntries + (DWORD)pResourceDirectory->NumberOfIdEntries;
+
+ for (DWORD iEntry = 0; iEntry < iEntryCount; iEntry++)
+ {
+ DWORD nameUInt;
+ NewArrayHolder<WCHAR> nameString;
+ if (!ReadNameFromResourceDirectoryEntry(pDecoder, rvaOfResourceSection, pDirectoryEntries, iEntry, &nameUInt, &nameString))
+ return false;
+
+ LPCWSTR name = MAKEINTRESOURCEW(nameUInt);
+ if (nameString != NULL)
+ name = &nameString[0];
+
+ bool isDirectory = !!(pDirectoryEntries[iEntry].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY);
+ DWORD offsetToData = pDirectoryEntries[iEntry].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY;
+ DWORD dataRva = rvaOfResourceSection + offsetToData;
+
+ if (!resourceTableEnumerator(pDecoder, rvaOfResourceSection, isDirectory, name, dataRva, context))
+ return false;
+ }
+
+ return true;
+}
+
+bool DoesResourceNameMatch(LPCWSTR nameA, LPCWSTR nameB)
+{
+ bool foundEntry = false;
+
+ if (IS_INTRESOURCE(nameA))
+ {
+ // name is id
+ if (nameA == nameB)
+ foundEntry = true;
+ }
+ else
+ {
+ // name is a string.
+
+ // Check for name enumerated is an id. If so, it doesn't match, skip to next.
+ if (IS_INTRESOURCE(nameB))
+ return false;
+ else
+ foundEntry = !wcscmp(nameB, nameA);
+ }
+
+ return foundEntry;
+}
+
+bool EnumerateLangIDs(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (isDirectory)
+ return false;
+
+ // Only LangIDs are permitted here
+ if (!IS_INTRESOURCE(name))
+ return false;
+
+ if (dataRVA == 0)
+ return false;
+
+ COUNT_T cbData;
+ DWORD resourceDataRva = ReadResourceDataEntry(pDecoder, dataRVA, &cbData);
+ if (!pDecoder->CheckRva(resourceDataRva, cbData))
+ {
+ return false;
+ }
+
+ BYTE *pData = (BYTE*)pDecoder->GetRvaData(resourceDataRva);
+
+ return state->langIDcallback(state->nameName, state->nameType, (DWORD)(uintptr_t)name, pData, cbData, state->context);
+}
+
+
+bool EnumerateNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ state->nameName = name;
+ return state->namesCallback(state->nameName, state->nameType, state->context);
+}
+
+bool EnumerateNamesForLangID(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ bool foundEntry = DoesResourceNameMatch(state->nameName, name);
+
+ if (foundEntry)
+ return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerLangID, context);
+ else
+ return true; // Keep scanning
+}
+
+
+bool EnumerateTypes(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateTypesState *state = (ResourceEnumerateTypesState*)context;
+ if (!isDirectory)
+ return false;
+
+ state->nameType = name;
+ return state->callback(name, state->context);
+}
+
+bool EnumerateTypesForNames(const PEDecoder *pDecoder, DWORD rvaOfResourceSection, bool isDirectory, LPCWSTR name, DWORD dataRVA, void *context)
+{
+ ResourceEnumerateNamesState *state = (ResourceEnumerateNamesState*)context;
+ if (!isDirectory)
+ return false;
+
+ bool foundEntry = DoesResourceNameMatch(state->nameType, name);
+
+ if (foundEntry)
+ return EnumerateWin32ResourceTable(pDecoder, rvaOfResourceSection, dataRVA, state->callbackPerName, context);
+ else
+ return true; // Keep scanning
+}
+
+
+bool PEDecoder::EnumerateWin32ResourceTypes(PEDecoder_ResourceTypesCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateTypesState state;
+ state.context = context;
+ state.callback = callback;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypes, &state);
+}
+
+bool PEDecoder::EnumerateWin32ResourceNames(LPCWSTR lpType, PEDecoder_ResourceNamesCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateNamesState state;
+ state.context = context;
+ state.namesCallback = callback;
+ state.langIDcallback = NULL;
+ state.nameType = lpType;
+ state.nameName = NULL;
+ state.callbackPerName = EnumerateNames;
+ state.callbackPerLangID = NULL;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state);
+}
+
+bool PEDecoder::EnumerateWin32Resources(LPCWSTR lpName, LPCWSTR lpType, PEDecoder_ResourceCallbackFunction callback, void* context) const
+{
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE))
+ return true;
+
+ COUNT_T resourceDataSize = 0;
+ IMAGE_DATA_DIRECTORY *pDir = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE);
+
+ if (pDir->VirtualAddress == 0)
+ return true;
+
+ DWORD rvaOfResourceSection = pDir->VirtualAddress;
+
+ ResourceEnumerateNamesState state;
+ state.context = context;
+ state.namesCallback = NULL;
+ state.langIDcallback = callback;
+ state.nameType = lpType;
+ state.nameName = lpName;
+ state.callbackPerName = EnumerateNamesForLangID;
+ state.callbackPerLangID = EnumerateLangIDs;
+
+ return EnumerateWin32ResourceTable(this, rvaOfResourceSection, rvaOfResourceSection, EnumerateTypesForNames, &state);
+}
+
+BOOL PEDecoder::HasNativeHeader() const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+#ifdef FEATURE_PREJIT
+ // Pretend that ready-to-run images do not have native header
+ RETURN (GetCorHeader() && ((GetCorHeader()->Flags & VAL32(COMIMAGE_FLAGS_IL_LIBRARY)) != 0) && !HasReadyToRunHeader());
+#else
+ RETURN FALSE;
+#endif
+}
+
+CHECK PEDecoder::CheckNativeHeader() const
+{
+ CONTRACT_CHECK
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_CHECK_END;
+
+#ifdef FEATURE_PREJIT
+ if (m_flags & FLAG_NATIVE_CHECKED)
+ CHECK_OK;
+
+ CHECK(CheckCorHeader());
+
+ CHECK(HasNativeHeader());
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+
+ CHECK(CheckDirectory(pDir));
+ CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER));
+
+#if 0
+ // We want to be sure to not trigger these checks when loading a native
+ // image in a retail build
+
+ // And we do not want to trigger these checks in debug builds either to avoid debug/retail behavior
+ // differences.
+
+ PTR_CORCOMPILE_HEADER pHeader = PTR_CORCOMPILE_HEADER((TADDR)GetDirectoryData(pDir));
+
+ CHECK(CheckDirectory(&pHeader->EEInfoTable));
+ CHECK(pHeader->EEInfoTable.Size == sizeof(CORCOMPILE_EE_INFO_TABLE));
+
+ CHECK(CheckDirectory(&pHeader->HelperTable, 0, NULL_OK));
+ // @todo: verify helper table size
+
+ CHECK(CheckDirectory(&pHeader->ImportSections, 0, NULL_OK));
+ // @todo verify import sections
+
+ CHECK(CheckDirectory(&pHeader->ImportTable, 0, NULL_OK));
+ // @todo verify import table
+
+ CHECK(CheckDirectory(&pHeader->VersionInfo, 0, NULL_OK)); // no version header for precompiled netmodules
+ CHECK(pHeader->VersionInfo.Size == 0
+ || (pHeader->VersionInfo.Size == sizeof(CORCOMPILE_VERSION_INFO) &&
+ // Sanity check that we are not just pointing to zeroed-out memory
+ ((CORCOMPILE_VERSION_INFO*)PTR_READ(GetDirectoryData(&pHeader->VersionInfo), sizeof(CORCOMPILE_VERSION_INFO)))->wOSMajorVersion != 0));
+
+ CHECK(CheckDirectory(&pHeader->Dependencies, 0, NULL_OK)); // no version header for precompiled netmodules
+ CHECK(pHeader->Dependencies.Size % sizeof(CORCOMPILE_DEPENDENCY) == 0);
+
+ CHECK(CheckDirectory(&pHeader->DebugMap, 0, NULL_OK));
+ CHECK(pHeader->DebugMap.Size % sizeof(CORCOMPILE_DEBUG_RID_ENTRY) == 0);
+
+ // GetPersistedModuleImage()
+ CHECK(CheckDirectory(&pHeader->ModuleImage));
+ CHECK(pHeader->ModuleImage.Size > 0); // sizeof(Module) if we knew it here
+
+ CHECK(CheckDirectory(&pHeader->CodeManagerTable));
+ CHECK(pHeader->CodeManagerTable.Size == sizeof(CORCOMPILE_CODE_MANAGER_ENTRY));
+
+ PTR_CORCOMPILE_CODE_MANAGER_ENTRY pEntry = PTR_CORCOMPILE_CODE_MANAGER_ENTRY((TADDR)GetDirectoryData(&pHeader->CodeManagerTable));
+ CHECK(CheckDirectory(&pEntry->HotCode, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pEntry->Code, IMAGE_SCN_MEM_WRITE, NULL_OK));
+ CHECK(CheckDirectory(&pEntry->ColdCode, IMAGE_SCN_MEM_WRITE, NULL_OK));
+
+ CHECK(CheckDirectory(&pHeader->ProfileDataList, 0, NULL_OK));
+ CHECK(pHeader->ProfileDataList.Size >= sizeof(CORCOMPILE_METHOD_PROFILE_LIST)
+ || pHeader->ProfileDataList.Size == 0);
+
+#endif
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_NATIVE_CHECKED;
+
+#else // FEATURE_PREJIT
+ CHECK(false);
+#endif // FEATURE_PREJIT
+
+ CHECK_OK;
+}
+
+READYTORUN_HEADER * PEDecoder::FindReadyToRunHeader() const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+
+ if (VAL32(pDir->Size) >= sizeof(READYTORUN_HEADER) && CheckDirectory(pDir))
+ {
+ PTR_READYTORUN_HEADER pHeader = PTR_READYTORUN_HEADER((TADDR)GetDirectoryData(pDir));
+ if (pHeader->Signature == READYTORUN_SIGNATURE)
+ {
+ const_cast<PEDecoder*>(this)->m_pReadyToRunHeader = pHeader;
+ return pHeader;
+ }
+ }
+
+ const_cast<PEDecoder *>(this)->m_flags |= FLAG_HAS_NO_READYTORUN_HEADER;
+ return NULL;
+}
+
+#ifndef DACCESS_COMPILE
+void *PEDecoder::GetExport(LPCSTR exportName) const
+{
+ // Get the export directory entry
+ PIMAGE_DATA_DIRECTORY pExportDirectoryEntry = GetDirectoryEntry(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ if (pExportDirectoryEntry->VirtualAddress == 0 || pExportDirectoryEntry->Size == 0)
+ {
+ return NULL;
+ }
+
+ uint8_t *imageBase = (uint8_t *)GetBase();
+ const IMAGE_EXPORT_DIRECTORY *pExportDir = (const IMAGE_EXPORT_DIRECTORY *)GetDirectoryData(pExportDirectoryEntry);
+
+ uint32_t namePointerCount = VAL32(pExportDir->NumberOfNames);
+ uint32_t addressTableRVA = VAL32(pExportDir->AddressOfFunctions);
+ uint32_t namePointersRVA = VAL32(pExportDir->AddressOfNames);
+
+ for (uint32_t nameIndex = 0; nameIndex < namePointerCount; nameIndex++)
+ {
+ uint32_t namePointerRVA = VAL32(*(const uint32_t *)&imageBase[namePointersRVA + sizeof(uint32_t) * nameIndex]);
+ if (namePointerRVA != 0)
+ {
+ const char *namePointer = (const char *)&imageBase[namePointerRVA];
+ if (!strcmp(namePointer, exportName))
+ {
+ uint32_t exportRVA = VAL32(*(const uint32_t *)&imageBase[addressTableRVA + sizeof(uint32_t) * nameIndex]);
+ return &imageBase[exportRVA];
+ }
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+//
+// code:PEDecoder::CheckILMethod and code:PEDecoder::ComputeILMethodSize really belong to
+// file:..\inc\corhlpr.cpp. Unfortunately, corhlpr.cpp is public header file that cannot be
+// properly DACized and have other dependencies on the rest of the CLR.
+//
+
+typedef DPTR(COR_ILMETHOD_TINY) PTR_COR_ILMETHOD_TINY;
+typedef DPTR(COR_ILMETHOD_FAT) PTR_COR_ILMETHOD_FAT;
+typedef DPTR(COR_ILMETHOD_SECT_SMALL) PTR_COR_ILMETHOD_SECT_SMALL;
+typedef DPTR(COR_ILMETHOD_SECT_FAT) PTR_COR_ILMETHOD_SECT_FAT;
+
+CHECK PEDecoder::CheckILMethod(RVA rva)
+{
+ CONTRACT_CHECK
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ //
+ // Incrementaly validate that the entire IL method body is within the bounds of the image
+ //
+
+ // We need to have at least the tiny header
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY)));
+
+ TADDR pIL = GetRvaData(rva);
+
+ PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL);
+
+ if (pMethodTiny->IsTiny())
+ {
+ // Tiny header has no optional sections - we are done.
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize()));
+ CHECK_OK;
+ }
+
+ //
+ // Fat header
+ //
+
+ CHECK(CheckRva(rva, sizeof(IMAGE_COR_ILMETHOD_FAT)));
+
+ PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL);
+
+ CHECK(pMethodFat->IsFat());
+
+ S_UINT32 codeEnd = S_UINT32(4) * S_UINT32(pMethodFat->GetSize()) + S_UINT32(pMethodFat->GetCodeSize());
+ CHECK(!codeEnd.IsOverflow());
+
+ // Check minimal size of the header
+ CHECK(pMethodFat->GetSize() >= (sizeof(COR_ILMETHOD_FAT) / 4));
+
+ CHECK(CheckRva(rva, codeEnd.Value()));
+
+ if (!pMethodFat->More())
+ {
+ CHECK_OK;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::GetSect
+ TADDR pSect = AlignUp(pIL + codeEnd.Value(), 4);
+
+ //
+ // Optional sections following the code
+ //
+
+ for (;;)
+ {
+ CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_SMALL)));
+
+ PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect);
+
+ UINT32 sectSize;
+
+ if (pSectSmall->IsSmall())
+ {
+ sectSize = pSectSmall->DataSize;
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL));
+ }
+ else
+ {
+ CHECK(CheckRva(rva, UINT32(pSect - pIL) + sizeof(IMAGE_COR_ILMETHOD_SECT_FAT)));
+
+ PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect);
+
+ sectSize = pSectFat->GetDataSize();
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT));
+ }
+
+ // Section has to be non-empty to avoid infinite loop below
+ CHECK(sectSize > 0);
+
+ S_UINT32 sectEnd = S_UINT32(UINT32(pSect - pIL)) + S_UINT32(sectSize);
+ CHECK(!sectEnd.IsOverflow());
+
+ CHECK(CheckRva(rva, sectEnd.Value()));
+
+ if (!pSectSmall->More())
+ {
+ CHECK_OK;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::Next
+ pSect = AlignUp(pIL + sectEnd.Value(), 4);
+ }
+}
+
+//
+// Compute size of IL blob. Assumes that the IL is within the bounds of the image - make sure
+// to call code:PEDecoder::CheckILMethod before calling this method.
+//
+// code:PEDecoder::ComputeILMethodSize is DACized duplicate of code:COR_ILMETHOD_DECODER::GetOnDiskSize.
+// code:MethodDesc::GetILHeader contains debug-only check that ensures that both implementations
+// are in sync.
+//
+
+SIZE_T PEDecoder::ComputeILMethodSize(TADDR pIL)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ } CONTRACTL_END;
+
+ //
+ // Mirror flow of code:PEDecoder::CheckILMethod, except for the range checks
+ //
+
+ PTR_COR_ILMETHOD_TINY pMethodTiny = PTR_COR_ILMETHOD_TINY(pIL);
+
+ if (pMethodTiny->IsTiny())
+ {
+ return sizeof(IMAGE_COR_ILMETHOD_TINY) + pMethodTiny->GetCodeSize();
+ }
+
+ PTR_COR_ILMETHOD_FAT pMethodFat = PTR_COR_ILMETHOD_FAT(pIL);
+
+ UINT32 codeEnd = 4 * pMethodFat->GetSize() + pMethodFat->GetCodeSize();
+
+ if (!pMethodFat->More())
+ {
+ return codeEnd;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::GetSect
+ TADDR pSect = AlignUp(pIL + codeEnd, 4);
+
+ for (;;)
+ {
+ PTR_COR_ILMETHOD_SECT_SMALL pSectSmall = PTR_COR_ILMETHOD_SECT_SMALL(pSect);
+
+ UINT32 sectSize;
+
+ if (pSectSmall->IsSmall())
+ {
+ sectSize = pSectSmall->DataSize;
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_SMALL::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_SMALL));
+ }
+ else
+ {
+ PTR_COR_ILMETHOD_SECT_FAT pSectFat = PTR_COR_ILMETHOD_SECT_FAT(pSect);
+
+ sectSize = pSectFat->GetDataSize();
+
+ // Workaround for bug in shipped compilers - see comment in code:COR_ILMETHOD_SECT::DataSize
+ if ((pSectSmall->Kind & CorILMethod_Sect_KindMask) == CorILMethod_Sect_EHTable)
+ sectSize = COR_ILMETHOD_SECT_EH_FAT::Size(sectSize / sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT));
+ }
+
+ UINT32 sectEnd = UINT32(pSect - pIL) + sectSize;
+
+ if (!pSectSmall->More() || (sectSize == 0))
+ {
+ return sectEnd;
+ }
+
+ // DACized copy of code:COR_ILMETHOD_FAT::Next
+ pSect = AlignUp(pIL + sectEnd, 4);
+ }
+}
+
+//
+// GetDebugDirectoryEntry - return the debug directory entry at the specified index
+//
+// Arguments:
+// index The 0-based index of the entry to return. Usually this is just 0,
+// but there can be multiple debug directory entries in a PE file.
+//
+// Return value:
+// A pointer to the IMAGE_DEBUG_DIRECTORY in the PE file for the specified index,
+// or NULL if it doesn't exist.
+//
+// Note that callers on untrusted input are required to validate the debug directory
+// first by calling CheckDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG) (possibly
+// indirectly via one of the CheckILOnly* functions).
+//
+PTR_IMAGE_DEBUG_DIRECTORY PEDecoder::GetDebugDirectoryEntry(UINT index) const
+{
+ CONTRACT(PTR_IMAGE_DEBUG_DIRECTORY)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (!HasDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG))
+ {
+ RETURN NULL;
+ }
+
+ // Get a pointer to the contents and size of the debug directory
+ // Also validates (in CHK builds) that this is all within one section, which the
+ // caller should have already validated if they don't trust the context of this PE file.
+ COUNT_T cbDebugDir;
+ TADDR taDebugDir = GetDirectoryEntryData(IMAGE_DIRECTORY_ENTRY_DEBUG, &cbDebugDir);
+
+ // Check if the specified directory entry exists (based on the size of the directory)
+ // Note that the directory size should be an even multiple of the entry size, but we
+ // just round-down because we need to be resiliant (without asserting) to corrupted /
+ // fuzzed PE files.
+ UINT cNumEntries = cbDebugDir / sizeof(IMAGE_DEBUG_DIRECTORY);
+ if (index >= cNumEntries)
+ {
+ RETURN NULL; // index out of range
+ }
+
+ // Get the debug directory entry at the specified index.
+ PTR_IMAGE_DEBUG_DIRECTORY pDebugEntry = dac_cast<PTR_IMAGE_DEBUG_DIRECTORY>(taDebugDir);
+ pDebugEntry += index; // offset from the first entry to the requested entry
+ RETURN pDebugEntry;
+}
+
+
+#ifdef FEATURE_PREJIT
+
+CORCOMPILE_EE_INFO_TABLE *PEDecoder::GetNativeEEInfoTable() const
+{
+ CONTRACT(CORCOMPILE_EE_INFO_TABLE *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ POSTCONDITION(CheckPointer(RETVAL));
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->EEInfoTable;
+
+ // 403523: PREFIX correctly complained here. Fixed GetDirectoryData.
+ RETURN PTR_CORCOMPILE_EE_INFO_TABLE(GetDirectoryData(pDir));
+}
+
+
+void *PEDecoder::GetNativeHelperTable(COUNT_T *pSize) const
+{
+ CONTRACT(void *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->HelperTable;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (void *)GetDirectoryData(pDir);
+}
+
+CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfoMaybeNull(bool skipCheckNativeHeader) const
+{
+ CONTRACT(CORCOMPILE_VERSION_INFO *)
+ {
+ PRECONDITION(skipCheckNativeHeader || CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VersionInfo;
+
+ RETURN PTR_CORCOMPILE_VERSION_INFO(GetDirectoryData(pDir));
+}
+
+CORCOMPILE_VERSION_INFO *PEDecoder::GetNativeVersionInfo() const
+{
+ CONTRACT(CORCOMPILE_VERSION_INFO *)
+ {
+ POSTCONDITION(CheckPointer(RETVAL));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ RETURN GetNativeVersionInfoMaybeNull();
+}
+
+BOOL PEDecoder::HasNativeDebugMap() const
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckNativeHeader());
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ // 403522: Prefix complained correctly here.
+ CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader();
+ if (!pNativeHeader)
+ RETURN FALSE;
+ else
+ RETURN (VAL32(pNativeHeader->DebugMap.VirtualAddress) != 0);
+}
+
+TADDR PEDecoder::GetNativeDebugMap(COUNT_T *pSize) const
+{
+ CONTRACT(TADDR)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->DebugMap;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (GetDirectoryData(pDir));
+}
+
+Module *PEDecoder::GetPersistedModuleImage(COUNT_T *pSize) const
+{
+ CONTRACT(Module *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ModuleImage;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN (Module *) GetDirectoryData(pDir);
+}
+
+CHECK PEDecoder::CheckNativeHeaderVersion() const
+{
+ CONTRACT_CHECK
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetCorHeader()->ManagedNativeHeader;
+ CHECK(VAL32(pDir->Size) == sizeof(CORCOMPILE_HEADER));
+
+ CORCOMPILE_HEADER *pNativeHeader = GetNativeHeader();
+ CHECK(pNativeHeader->Signature == CORCOMPILE_SIGNATURE);
+ CHECK(pNativeHeader->MajorVersion == CORCOMPILE_MAJOR_VERSION);
+ CHECK(pNativeHeader->MinorVersion == CORCOMPILE_MINOR_VERSION);
+
+ CHECK_OK;
+}
+
+CORCOMPILE_CODE_MANAGER_ENTRY *PEDecoder::GetNativeCodeManagerTable() const
+{
+ CONTRACT(CORCOMPILE_CODE_MANAGER_ENTRY *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->CodeManagerTable;
+
+ RETURN PTR_CORCOMPILE_CODE_MANAGER_ENTRY(GetDirectoryData(pDir));
+}
+
+PCODE PEDecoder::GetNativeHotCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->HotCode;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+PCODE PEDecoder::GetNativeCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ SUPPORTS_DAC;
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->Code;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+PCODE PEDecoder::GetNativeColdCode(COUNT_T * pSize) const
+{
+ CONTRACT(PCODE)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeCodeManagerTable()->ColdCode;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN GetDirectoryData(pDir);
+}
+
+
+CORCOMPILE_METHOD_PROFILE_LIST *PEDecoder::GetNativeProfileDataList(COUNT_T * pSize) const
+{
+ CONTRACT(CORCOMPILE_METHOD_PROFILE_LIST *)
+ {
+ PRECONDITION(CheckNativeHeader());
+ PRECONDITION(CheckPointer(pSize, NULL_OK));
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ProfileDataList;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN PTR_CORCOMPILE_METHOD_PROFILE_LIST(GetDirectoryData(pDir));
+}
+
+#endif // FEATURE_PREJIT
+
+PTR_CVOID PEDecoder::GetNativeManifestMetadata(COUNT_T *pSize) const
+{
+ CONTRACT(PTR_CVOID)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(HasReadyToRunHeader() || CheckNativeHeader());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK)); // TBD - may not store metadata for IJW
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = NULL;
+#ifdef FEATURE_PREJIT
+ if (!HasReadyToRunHeader())
+ {
+ pDir = GetMetaDataHelper(METADATA_SECTION_MANIFEST);
+ }
+ else
+#endif
+ {
+ READYTORUN_HEADER * pHeader = GetReadyToRunHeader();
+
+ PTR_READYTORUN_SECTION pSections = dac_cast<PTR_READYTORUN_SECTION>(dac_cast<TADDR>(pHeader) + sizeof(READYTORUN_HEADER));
+ for (DWORD i = 0; i < pHeader->CoreHeader.NumberOfSections; i++)
+ {
+ // Verify that section types are sorted
+ _ASSERTE(i == 0 || (pSections[i - 1].Type < pSections[i].Type));
+
+ READYTORUN_SECTION * pSection = pSections + i;
+ if (pSection->Type == ReadyToRunSectionType::ManifestMetadata)
+ {
+ // Set pDir to the address of the manifest metadata section
+ pDir = &pSection->Section;
+ break;
+ }
+ }
+
+ // ReadyToRun file without large version bubble support doesn't have the ManifestMetadata
+ if (pDir == NULL)
+ {
+ if (pSize != NULL)
+ {
+ *pSize = 0;
+ }
+
+ RETURN NULL;
+ }
+ }
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ RETURN dac_cast<PTR_VOID>(GetDirectoryData(pDir));
+}
+
+#ifdef FEATURE_PREJIT
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSections(COUNT_T *pCount) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ if (pCount != NULL)
+ *pCount = VAL32(pDir->Size) / sizeof(CORCOMPILE_IMPORT_SECTION);
+
+ RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir));
+}
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionFromIndex(COUNT_T index) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0);
+ _ASSERTE(index * sizeof(CORCOMPILE_IMPORT_SECTION) < VAL32(pDir->Size));
+
+ RETURN dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir)) + index;
+}
+
+PTR_CORCOMPILE_IMPORT_SECTION PEDecoder::GetNativeImportSectionForRVA(RVA rva) const
+{
+ CONTRACT(PTR_CORCOMPILE_IMPORT_SECTION)
+ {
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->ImportSections;
+
+ _ASSERTE(VAL32(pDir->Size) % sizeof(CORCOMPILE_IMPORT_SECTION) == 0);
+
+ PTR_CORCOMPILE_IMPORT_SECTION pSections = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(GetDirectoryData(pDir));
+ PTR_CORCOMPILE_IMPORT_SECTION pEnd = dac_cast<PTR_CORCOMPILE_IMPORT_SECTION>(dac_cast<TADDR>(pSections) + VAL32(pDir->Size));
+
+ for (PTR_CORCOMPILE_IMPORT_SECTION pSection = pSections; pSection < pEnd; pSection++)
+ {
+ if (rva >= VAL32(pSection->Section.VirtualAddress) && rva < VAL32(pSection->Section.VirtualAddress) + VAL32(pSection->Section.Size))
+ RETURN pSection;
+ }
+
+ RETURN NULL;
+}
+
+TADDR PEDecoder::GetStubsTable(COUNT_T *pSize) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->StubsData;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ return (GetDirectoryData(pDir));
+}
+
+TADDR PEDecoder::GetVirtualSectionsTable(COUNT_T *pSize) const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckNativeHeader());
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ IMAGE_DATA_DIRECTORY *pDir = &GetNativeHeader()->VirtualSectionsTable;
+
+ if (pSize != NULL)
+ *pSize = VAL32(pDir->Size);
+
+ return (GetDirectoryData(pDir));
+}
+
+#endif // FEATURE_PREJIT
+
+// Get the SizeOfStackReserve and SizeOfStackCommit from the PE file that was used to create
+// the calling process (.exe file).
+void PEDecoder::GetEXEStackSizes(SIZE_T *PE_SizeOfStackReserve, SIZE_T *PE_SizeOfStackCommit) const
+{
+ CONTRACTL {
+ PRECONDITION(!IsDll()); // This routine should only be called for EXE files.
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ * PE_SizeOfStackReserve = GetSizeOfStackReserve();
+ * PE_SizeOfStackCommit = GetSizeOfStackCommit();
+}
+
+CHECK PEDecoder::CheckWillCreateGuardPage() const
+{
+ CONTRACT_CHECK
+ {
+ PRECONDITION(CheckNTHeaders());
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_CHECK_END;
+
+ if (!IsDll())
+ {
+ SIZE_T sizeReservedStack = 0;
+ SIZE_T sizeCommitedStack = 0;
+
+ GetEXEStackSizes(&sizeReservedStack, &sizeCommitedStack);
+
+ CHECK(ThreadWillCreateGuardPage(sizeReservedStack, sizeCommitedStack));
+
+ }
+
+ CHECK_OK;
+}
+
+BOOL PEDecoder::HasNativeEntryPoint() const
+{
+ CONTRACTL {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ } CONTRACTL_END;
+
+ ULONG flags = GetCorHeader()->Flags;
+ return ((flags & VAL32(COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)) &&
+ (IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken) != VAL32(0)));
+}
+
+void *PEDecoder::GetNativeEntryPoint() const
+{
+ CONTRACT (void *) {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckCorHeader());
+ PRECONDITION(HasNativeEntryPoint());
+ POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
+ } CONTRACT_END;
+
+ RETURN ((void *) GetRvaData((RVA)VAL32(IMAGE_COR20_HEADER_FIELD(*GetCorHeader(), EntryPointToken))));
+}
+
+#ifdef DACCESS_COMPILE
+
+void
+PEDecoder::EnumMemoryRegions(CLRDataEnumMemoryFlags flags,
+ bool enumThis)
+{
+ SUPPORTS_DAC;
+ if (enumThis)
+ {
+ DAC_ENUM_DTHIS();
+ }
+
+ DacEnumMemoryRegion((TADDR)m_base, sizeof(IMAGE_DOS_HEADER));
+ m_pNTHeaders.EnumMem();
+ m_pCorHeader.EnumMem();
+ m_pNativeHeader.EnumMem();
+ m_pReadyToRunHeader.EnumMem();
+
+ if (HasNTHeaders())
+ {
+ // resource file does not have NT Header.
+ //
+ // we also need to write out section header.
+ DacEnumMemoryRegion(dac_cast<TADDR>(FindFirstSection()), sizeof(IMAGE_SECTION_HEADER) * GetNumberOfSections());
+ }
+}
+
+#endif // #ifdef DACCESS_COMPILE
+
+// --------------------------------------------------------------------------------
+
+#ifdef _DEBUG
+
+// This is a stress mode to force DLLs to be relocated.
+// This is particularly useful for hardbinding of ngen images as we
+// embed pointers into other hardbound ngen dependencies.
+
+BOOL PEDecoder::GetForceRelocs()
+{
+ WRAPPER_NO_CONTRACT;
+
+ static ConfigDWORD forceRelocs;
+ return (forceRelocs.val(CLRConfig::INTERNAL_ForceRelocs) != 0);
+}
+
+BOOL PEDecoder::ForceRelocForDLL(LPCWSTR lpFileName)
+{
+#ifdef _DEBUG
+ STATIC_CONTRACT_NOTHROW; \
+ ANNOTATION_DEBUG_ONLY; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+#endif
+
+#if defined(DACCESS_COMPILE) || defined(TARGET_UNIX)
+ return TRUE;
+#else
+
+ if (!GetForceRelocs())
+ return TRUE;
+
+ BOOL fSuccess = FALSE;
+ PBYTE hndle = NULL;
+ PEDecoder pe;
+ void* pPreferredBase;
+ COUNT_T nVirtualSize;
+
+ HANDLE hFile = WszCreateFile(lpFileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ goto ErrExit;
+
+ HANDLE hMap = WszCreateFileMapping(hFile,
+ NULL,
+ SEC_IMAGE | PAGE_READONLY,
+ 0,
+ 0,
+ NULL);
+ CloseHandle(hFile);
+
+ if (hMap == NULL)
+ goto ErrExit;
+
+ hndle = (PBYTE)MapViewOfFile(hMap,
+ FILE_MAP_READ,
+ 0,
+ 0,
+ 0);
+ CloseHandle(hMap);
+
+ if (!hndle)
+ goto ErrExit;
+
+ pe.Init(hndle);
+
+ pPreferredBase = (void*)pe.GetPreferredBase();
+ nVirtualSize = pe.GetVirtualSize();
+
+ UnmapViewOfFile(hndle);
+ hndle = NULL;
+
+ // Reserve the space so nobody can use it. A potential bug is likely to
+ // result in a plain AV this way. It is not a good idea to use the original
+ // mapping for the reservation since since it would lock the file on the disk.
+ if (!ClrVirtualAlloc(pPreferredBase, nVirtualSize, MEM_RESERVE, PAGE_NOACCESS))
+ goto ErrExit;
+
+ fSuccess = TRUE;
+
+ErrExit:
+ if (hndle != NULL)
+ UnmapViewOfFile(hndle);
+
+ return fSuccess;
+
+#endif // DACCESS_COMPILE || TARGET_UNIX
+}
+
+#endif // _DEBUG
+
+//
+// MethodSectionIterator class is used to iterate hot (or) cold method section in an ngen image.
+// Also used to iterate over jitted methods in the code heap
+//
+MethodSectionIterator::MethodSectionIterator(const void *code, SIZE_T codeSize,
+ const void *codeTable, SIZE_T codeTableSize)
+{
+ //For DAC builds,we'll read the table one DWORD at a time. Note that m_code IS
+ //NOT a host pointer.
+ m_codeTableStart = PTR_DWORD(TADDR(codeTable));
+ m_codeTable = m_codeTableStart;
+ _ASSERTE((codeTableSize % sizeof(DWORD)) == 0);
+ m_codeTableEnd = m_codeTableStart + (codeTableSize / sizeof(DWORD));
+ m_code = (BYTE *) code;
+ m_current = NULL;
+
+
+ if (m_codeTable < m_codeTableEnd)
+ {
+ m_dword = *m_codeTable++;
+ m_index = 0;
+ }
+ else
+ {
+ m_index = NIBBLES_PER_DWORD;
+ }
+}
+
+BOOL MethodSectionIterator::Next()
+{
+ while (m_codeTable < m_codeTableEnd || m_index < (int)NIBBLES_PER_DWORD)
+ {
+ while (m_index++ < (int)NIBBLES_PER_DWORD)
+ {
+ int nibble = (m_dword & HIGHEST_NIBBLE_MASK)>>HIGHEST_NIBBLE_BIT;
+ m_dword <<= NIBBLE_SIZE;
+
+ if (nibble != 0)
+ {
+ // We have found a method start
+ m_current = m_code + ((nibble-1)*CODE_ALIGN);
+ m_code += BYTES_PER_BUCKET;
+ return TRUE;
+ }
+
+ m_code += BYTES_PER_BUCKET;
+ }
+
+ if (m_codeTable < m_codeTableEnd)
+ {
+ m_dword = *m_codeTable++;
+ m_index = 0;
+ }
+ }
+ return FALSE;
+}
diff --git a/src/coreclr/utilcode/peinformation.cpp b/src/coreclr/utilcode/peinformation.cpp
new file mode 100644
index 00000000000..78e41b11196
--- /dev/null
+++ b/src/coreclr/utilcode/peinformation.cpp
@@ -0,0 +1,98 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// --------------------------------------------------------------------------------
+// PEInformation.cpp
+//
+
+// --------------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "peinformation.h"
+
+
+HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, PEKIND * pPeKind)
+{
+ return TranslatePEToArchitectureType(CLRPeKind, dwImageType, 0, pPeKind);
+}
+
+HRESULT TranslatePEToArchitectureType(CorPEKind CLRPeKind, DWORD dwImageType, DWORD dwAssemblyFlags, PEKIND * pPeKind)
+{
+ HRESULT hr = S_OK;
+
+ _ASSERTE(pPeKind != NULL);
+
+ if (CLRPeKind == peNot)
+ { // Not a PE. Shouldn't ever get here.
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ else if (IsAfPA_NoPlatform(dwAssemblyFlags))
+ {
+ *pPeKind = peNone;
+ goto Exit;
+ }
+ else
+ {
+ if ((CLRPeKind & peILonly) &&
+ !(CLRPeKind & pe32Plus) &&
+ !(CLRPeKind & pe32BitRequired) &&
+ (dwImageType == IMAGE_FILE_MACHINE_I386))
+ {
+ // Processor-agnostic (MSIL)
+ *pPeKind = peMSIL;
+ }
+ else if (CLRPeKind & pe32Plus)
+ {
+ // 64-bit
+
+ if (CLRPeKind & pe32BitRequired)
+ {
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+
+ // Regardless of whether ILONLY is set or not, the architecture
+ // is the machine type.
+
+ if (dwImageType == IMAGE_FILE_MACHINE_IA64)
+ {
+ *pPeKind = peIA64;
+ }
+ else if (dwImageType == IMAGE_FILE_MACHINE_AMD64)
+ {
+ *pPeKind = peAMD64;
+ }
+ else
+ { // We don't support other architectures
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ }
+ else
+ {
+ // 32-bit, non-agnostic
+
+ if (dwImageType == IMAGE_FILE_MACHINE_I386)
+ {
+ *pPeKind = peI386;
+ }
+ else if (dwImageType == IMAGE_FILE_MACHINE_ARMNT)
+ {
+ *pPeKind = peARM;
+ }
+ else
+ { // Not supported
+ *pPeKind = peInvalid;
+ hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
+ goto Exit;
+ }
+ }
+ }
+
+Exit:
+ return hr;
+}
diff --git a/src/coreclr/utilcode/posterror.cpp b/src/coreclr/utilcode/posterror.cpp
new file mode 100644
index 00000000000..c71d1b82e6c
--- /dev/null
+++ b/src/coreclr/utilcode/posterror.cpp
@@ -0,0 +1,339 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// PostErrors.cpp
+//
+// This module contains the error handling/posting code for the engine. It
+// is assumed that all methods may be called by a dispatch client, and therefore
+// errors are always posted using IErrorInfo.
+//
+
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+#include "../dlls/mscorrc/resource.h"
+#include "ex.h"
+
+#include <posterror.h>
+
+#if !defined(lengthof)
+#define lengthof(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+// Local prototypes.
+HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext);
+
+void GetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
+{
+ WRAPPER_NO_CONTRACT;
+ CCompRC::GetDefaultCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId
+ );
+}
+//*****************************************************************************
+// Set callbacks to get culture info
+//*****************************************************************************
+void SetResourceCultureCallbacks(
+ FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
+ FPGETTHREADUICULTUREID fpGetThreadUICultureId // TODO: Don't rely on the LCID, only the name
+)
+{
+ WRAPPER_NO_CONTRACT;
+ CCompRC::SetDefaultCallbacks(
+ fpGetThreadUICultureNames,
+ fpGetThreadUICultureId
+ );
+
+}
+
+//*****************************************************************************
+// Public function to load a resource string
+//*****************************************************************************
+STDAPI UtilLoadStringRC(
+ UINT iResourceID,
+ __out_ecount(iMax) LPWSTR szBuffer,
+ int iMax,
+ int bQuiet
+)
+{
+ WRAPPER_NO_CONTRACT;
+ return UtilLoadResourceString(bQuiet? CCompRC::Optional : CCompRC::Required,iResourceID, szBuffer, iMax);
+}
+
+HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResourceID, __out_ecount (iMax) LPWSTR szBuffer, int iMax)
+{
+ CONTRACTL
+ {
+ DISABLED(NOTHROW);
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT retVal = E_OUTOFMEMORY;
+
+ SString::Startup();
+ EX_TRY
+ {
+ CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll();
+
+ if (pResourceDLL != NULL)
+ {
+ retVal = pResourceDLL->LoadString(eCategory, iResourceID, szBuffer, iMax);
+ }
+ }
+ EX_CATCH
+ {
+ // Catch any errors and return E_OUTOFMEMORY;
+ retVal = E_OUTOFMEMORY;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return retVal;
+}
+
+//*****************************************************************************
+// Format a Runtime Error message.
+//*****************************************************************************
+HRESULT __cdecl FormatRuntimeErrorVa(
+ __inout_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
+ ULONG cchMsg, // Size of buffer, characters.
+ HRESULT hrRpt, // The HR to report.
+ va_list marker) // Optional args.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcBuf[512]; // Resource string.
+ HRESULT hr;
+
+ // Ensure nul termination.
+ *rcMsg = W('\0');
+
+ // If this is one of our errors or if it is simply a resource ID, then grab the error from the rc file.
+ if ((HRESULT_FACILITY(hrRpt) == FACILITY_URT) || (HIWORD(hrRpt) == 0))
+ {
+ hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true);
+ if (hr == S_OK)
+ {
+ _vsnwprintf_s(rcMsg, cchMsg, _TRUNCATE, rcBuf, marker);
+ }
+ }
+ // Otherwise it isn't one of ours, so we need to see if the system can
+ // find the text for it.
+ else
+ {
+ if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ 0, hrRpt, 0,
+ rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/))
+ {
+ hr = S_OK;
+
+ // System messages contain a trailing \r\n, which we don't want normally.
+ size_t iLen = wcslen(rcMsg);
+ if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n')
+ rcMsg[iLen - 2] = '\0';
+ }
+ else
+ hr = HRESULT_FROM_GetLastError();
+ }
+
+ // If we failed to find the message anywhere, then issue a hard coded message.
+ if (FAILED(hr))
+ {
+ _snwprintf_s(rcMsg, cchMsg, _TRUNCATE, W("Common Language Runtime Internal error: 0x%08x"), hrRpt);
+ DEBUG_STMT(DbgWriteEx(rcMsg));
+ }
+
+ return hrRpt;
+} // FormatRuntimeErrorVa
+
+//*****************************************************************************
+// Format a Runtime Error message, varargs.
+//*****************************************************************************
+HRESULT __cdecl FormatRuntimeError(
+ __out_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
+ ULONG cchMsg, // Size of buffer, characters.
+ HRESULT hrRpt, // The HR to report.
+ ...) // Optional args.
+{
+ WRAPPER_NO_CONTRACT;
+ va_list marker; // User text.
+ va_start(marker, hrRpt);
+ hrRpt = FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
+ va_end(marker);
+ return hrRpt;
+}
+
+#ifdef FEATURE_COMINTEROP
+//*****************************************************************************
+// Create, fill out and set an error info object. Note that this does not fill
+// out the IID for the error object; that is done elsewhere.
+//*****************************************************************************
+HRESULT FillErrorInfo( // Return status.
+ LPCWSTR szMsg, // Error message.
+ DWORD dwHelpContext) // Help context.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ICreateErrorInfo *pICreateErr = NULL; // Error info creation Iface pointer.
+ IErrorInfo *pIErrInfo = NULL; // The IErrorInfo interface.
+ HRESULT hr; // Return status.
+
+ // Get the ICreateErrorInfo pointer.
+ hr = S_OK;
+ EX_TRY
+ {
+ hr = CreateErrorInfo(&pICreateErr);
+ }
+ EX_CATCH
+ {
+ hr = GET_EXCEPTION()->GetHR();
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ if (FAILED(hr))
+ return (hr);
+
+ // Set message text description.
+ if (FAILED(hr = pICreateErr->SetDescription((LPWSTR) szMsg)))
+ goto Exit1;
+
+ // suppress PreFast warning about passing literal string to non-const API.
+ // This API (ICreateErrorInfo::SetHelpFile) is documented to take a const argument, but
+ // we can't put const in the signature because it would break existing implementors of
+ // the API.
+#ifdef _PREFAST_
+#pragma prefast(push)
+#pragma warning(disable:6298)
+#endif
+
+ // Set the help file and help context.
+ //<TODO>@todo: we don't have a help file yet.</TODO>
+ if (FAILED(hr = pICreateErr->SetHelpFile(const_cast<WCHAR*>(W("complib.hlp")))) ||
+ FAILED(hr = pICreateErr->SetHelpContext(dwHelpContext)))
+ goto Exit1;
+
+#ifdef _PREFAST_
+#pragma prefast(pop)
+#endif
+
+ // Get the IErrorInfo pointer.
+ if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo)))
+ goto Exit1;
+
+ // Save the error and release our local pointers.
+ {
+ // If we get here, we have loaded oleaut32.dll.
+ CONTRACT_VIOLATION(ThrowsViolation);
+ SetErrorInfo(0L, pIErrInfo);
+ }
+
+Exit1:
+ pICreateErr->Release();
+ if (pIErrInfo) {
+ pIErrInfo->Release();
+ }
+ return hr;
+}
+#endif // FEATURE_COMINTEROP
+
+//*****************************************************************************
+// This function will post an error for the client. If the LOWORD(hrRpt) can
+// be found as a valid error message, then it is loaded and formatted with
+// the arguments passed in. If it cannot be found, then the error is checked
+// against FormatMessage to see if it is a system error. System errors are
+// not formatted so no add'l parameters are required. If any errors in this
+// process occur, hrRpt is returned for the client with no error posted.
+//*****************************************************************************
+extern "C"
+HRESULT __cdecl PostErrorVA( // Returned error.
+ HRESULT hrRpt, // Reported error.
+ va_list marker) // Error arguments.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ ENTRY_POINT;
+ }
+ CONTRACTL_END;
+
+#ifdef FEATURE_COMINTEROP
+
+ const DWORD cchMsg = 4096;
+ WCHAR *rcMsg = (WCHAR*)alloca(cchMsg * sizeof(WCHAR)); // Error message.
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // Return warnings without text.
+ if (!FAILED(hrRpt))
+ goto ErrExit;
+
+ // If we are already out of memory or out of stack or the thread is in some bad state,
+ // we don't want throw gasoline on the fire by calling ErrorInfo stuff below (which can
+ // trigger a delayload of oleaut32.dll). We don't need to embellish transient errors
+ // so just return this without text.
+ if (Exception::IsTransient(hrRpt))
+ {
+ goto ErrExit;
+ }
+
+ // Format the error.
+ FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
+
+ // Turn the error into a posted error message. If this fails, we still
+ // return the original error message since a message caused by our error
+ // handling system isn't going to give you a clue about the original error.
+ hr = FillErrorInfo(rcMsg, LOWORD(hrRpt));
+ _ASSERTE(hr == S_OK);
+
+ErrExit:
+
+ END_ENTRYPOINT_NOTHROW;
+
+#endif // FEATURE_COMINTEROP
+
+ return (hrRpt);
+} // PostErrorVA
+
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+
+//*****************************************************************************
+// This function will post an error for the client. If the LOWORD(hrRpt) can
+// be found as a valid error message, then it is loaded and formatted with
+// the arguments passed in. If it cannot be found, then the error is checked
+// against FormatMessage to see if it is a system error. System errors are
+// not formatted so no add'l parameters are required. If any errors in this
+// process occur, hrRpt is returned for the client with no error posted.
+//*****************************************************************************
+extern "C"
+HRESULT __cdecl PostError(
+ HRESULT hrRpt, // Reported error.
+ ...) // Error arguments.
+{
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+ WRAPPER_NO_CONTRACT;
+ va_list marker; // User text.
+ va_start(marker, hrRpt);
+ hrRpt = PostErrorVA(hrRpt, marker);
+ va_end(marker);
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+ return hrRpt;
+}
diff --git a/src/coreclr/utilcode/prettyprintsig.cpp b/src/coreclr/utilcode/prettyprintsig.cpp
new file mode 100644
index 00000000000..e902a5fc001
--- /dev/null
+++ b/src/coreclr/utilcode/prettyprintsig.cpp
@@ -0,0 +1,1027 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//*****************************************************************************
+// This code supports formatting a method and it's signature in a friendly
+// and consistent format.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "prettyprintsig.h"
+#include "utilcode.h"
+#include "metadata.h"
+#include "corpriv.h"
+
+/***********************************************************************/
+// Null-terminates the string held in "out"
+
+static WCHAR* asStringW(CQuickBytes *out)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
+ return 0;
+ WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
+ *cur = 0;
+ return((WCHAR*) out->Ptr());
+} // static WCHAR* asStringW()
+
+// Null-terminates the string held in "out"
+
+static CHAR* asStringA(CQuickBytes *out)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return NULL;);
+ }
+ CONTRACTL_END
+
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + 1)))
+ return 0;
+ CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
+ *cur = 0;
+ return((CHAR*) out->Ptr());
+} // static CHAR* asStringA()
+
+/***********************************************************************/
+// Appends the str to "out"
+// The string held in "out" is not NULL-terminated. asStringW() needs to
+// be called for the NULL-termination
+
+static HRESULT appendStrW(CQuickBytes *out, const WCHAR* str)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ SIZE_T len = wcslen(str) * sizeof(WCHAR);
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + len)))
+ return E_OUTOFMEMORY;
+ WCHAR * cur = (WCHAR *) ((BYTE *) out->Ptr() + oldSize);
+ memcpy(cur, str, len);
+ // Note no trailing null!
+ return S_OK;
+} // static HRESULT appendStrW()
+
+// Appends the str to "out"
+// The string held in "out" is not NULL-terminated. asStringA() needs to
+// be called for the NULL-termination
+
+static HRESULT appendStrA(CQuickBytes *out, const CHAR* str)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ SIZE_T len = strlen(str) * sizeof(CHAR);
+ SIZE_T oldSize = out->Size();
+ if (FAILED(out->ReSizeNoThrow(oldSize + len)))
+ return E_OUTOFMEMORY;
+ CHAR * cur = (CHAR *) ((BYTE *) out->Ptr() + oldSize);
+ memcpy(cur, str, len);
+ // Note no trailing null!
+ return S_OK;
+} // static HRESULT appendStrA()
+
+
+static HRESULT appendStrNumW(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ WCHAR buff[32];
+ swprintf_s(buff, 32, W("%d"), num);
+ return appendStrW(out, buff);
+} // static HRESULT appendStrNumW()
+
+static HRESULT appendStrNumA(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CHAR buff[32];
+ sprintf_s(buff, 32, "%d", num);
+ return appendStrA(out, buff);
+} // static HRESULT appendStrNumA()
+
+static HRESULT appendStrHexW(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ WCHAR buff[32];
+ swprintf_s(buff, 32, W("%08X"), num);
+ return appendStrW(out, buff);
+} // static HRESULT appendStrHexW()
+
+static HRESULT appendStrHexA(CQuickBytes *out, int num)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CHAR buff[32];
+ sprintf_s(buff, 32, "%08X", num);
+ return appendStrA(out, buff);
+} // static HRESULT appendStrHexA()
+
+/***********************************************************************/
+
+LPCWSTR PrettyPrintSigWorker(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI); // Import api to use.
+
+//*****************************************************************************
+//*****************************************************************************
+// pretty prints 'type' to the buffer 'out' returns a pointer to the next type,
+// or 0 on a format failure
+
+static PCCOR_SIGNATURE PrettyPrintType(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ size_t typeLen, // Maximum length of the type
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // ptr to IMDInternal class with ComSig
+{
+ mdToken tk;
+ const WCHAR * str;
+ WCHAR rcname[MAX_CLASS_NAME];
+ HRESULT hr;
+ unsigned __int8 elt = *typePtr++;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen;
+
+ switch(elt)
+ {
+ case ELEMENT_TYPE_VOID:
+ str = W("void");
+ goto APPEND;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ str = W("bool");
+ goto APPEND;
+
+ case ELEMENT_TYPE_CHAR:
+ str = W("wchar");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I1:
+ str = W("int8");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U1:
+ str = W("unsigned int8");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I2:
+ str = W("int16");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U2:
+ str = W("unsigned int16");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I4:
+ str = W("int32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U4:
+ str = W("unsigned int32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I8:
+ str = W("int64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U8:
+ str = W("unsigned int64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_R4:
+ str = W("float32");
+ goto APPEND;
+
+ case ELEMENT_TYPE_R8:
+ str = W("float64");
+ goto APPEND;
+
+ case ELEMENT_TYPE_U:
+ str = W("unsigned int");
+ goto APPEND;
+
+ case ELEMENT_TYPE_I:
+ str = W("int");
+ goto APPEND;
+
+ case ELEMENT_TYPE_OBJECT:
+ str = W("class System.Object");
+ goto APPEND;
+
+ case ELEMENT_TYPE_STRING:
+ str = W("class System.String");
+ goto APPEND;
+
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ str = W("class System.__Canon");
+ goto APPEND;
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ str = W("refany");
+ goto APPEND;
+
+ APPEND:
+ appendStrW(out, str);
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ str = W("value class ");
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CLASS:
+ str = W("class ");
+ goto DO_CLASS;
+
+ DO_CLASS:
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ appendStrW(out, str);
+ rcname[0] = 0;
+ str = rcname;
+
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ hr = pIMDI->GetTypeRefProps(tk, 0, rcname, NumItems(rcname), 0);
+ }
+ else if (TypeFromToken(tk) == mdtTypeDef)
+ {
+ hr = pIMDI->GetTypeDefProps(tk, rcname, NumItems(rcname), 0, 0, 0);
+ }
+ else
+ {
+ _ASSERTE(!"Unknown token type encountered in signature.");
+ str = W("<UNKNOWN>");
+ }
+
+ appendStrW(out, str);
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ appendStrW(out, W("[]"));
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ unsigned rank = CorSigUncompressData(typePtr);
+ PREFIX_ASSUME(rank <= 0xffffff);
+
+ // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0)
+ {
+ appendStrW(out, W("[??]"));
+ }
+ else
+ {
+ _ASSERTE(rank != 0);
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned int i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ lowerBounds[i] = CorSigUncompressData(typePtr);
+
+ appendStrW(out, W("["));
+ for(i = 0; i < rank; i++)
+ {
+ if (sizes[i] != 0 && lowerBounds[i] != 0)
+ {
+ appendStrNumW(out, lowerBounds[i]);
+ appendStrW(out, W("..."));
+ appendStrNumW(out, lowerBounds[i] + sizes[i] + 1);
+ }
+ if (i < rank-1)
+ appendStrW(out, W(","));
+ }
+ appendStrW(out, W("]"));
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_MVAR:
+ appendStrW(out, W("!!"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ appendStrW(out, W("!"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ unsigned ntypars = CorSigUncompressData(typePtr);
+ appendStrW(out, W("<"));
+ for (unsigned i = 0; i < ntypars; i++)
+ {
+ if (i > 0)
+ appendStrW(out, W(","));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ }
+ appendStrW(out, W(">"));
+ }
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ appendStrW(out, W("[module#"));
+ appendStrNumW(out, CorSigUncompressData(typePtr));
+ appendStrW(out, W(", token#"));
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ appendStrHexW(out, tk);
+ appendStrW(out, W("]"));
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ appendStrW(out, W("fnptr "));
+ PrettyPrintSigWorker(typePtr, (typeEnd - typePtr), W(""), out, pIMDI);
+ break;
+
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ appendStrW(out, W("native "));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ break;
+
+ // Modifiers or depedant types
+ case ELEMENT_TYPE_PINNED:
+ str = W(" pinned");
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_PTR:
+ str = W("*");
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_BYREF:
+ str = W("&");
+ goto MODIFIER;
+
+ MODIFIER:
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ appendStrW(out, str);
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL:
+ case ELEMENT_TYPE_END:
+ _ASSERTE(!"Unknown Type");
+ return(typePtr);
+ break;
+ }
+ return(typePtr);
+} // static PCCOR_SIGNATURE PrettyPrintType()
+
+//*****************************************************************************
+// Converts a com signature to a text signature.
+//
+// Note that this function DOES NULL terminate the result signature string.
+//*****************************************************************************
+LPCWSTR PrettyPrintSigLegacy(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ unsigned typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // Import api to use.
+{
+ return PrettyPrintSigWorker(typePtr, typeLen, name, out, pIMDI);
+} // LPCWSTR PrettyPrintSigLegacy()
+
+LPCWSTR PrettyPrintSigWorker(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const WCHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMetaDataImport * pIMDI) // Import api to use.
+{
+ out->Shrink(0);
+ unsigned numTyArgs = 0;
+ unsigned numArgs;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature.
+
+ if (name != 0) // 0 means a local var sig
+ {
+ // get the calling convention out
+ unsigned callConv = CorSigUncompressData(typePtr);
+
+ if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ if (name != 0 && *name != 0)
+ {
+ appendStrW(out, W(" "));
+ appendStrW(out, name);
+ }
+ return(asStringW(out));
+ }
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ appendStrW(out, W("instance "));
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ appendStrW(out, W("generic "));
+ numTyArgs = CorSigUncompressData(typePtr);
+ }
+
+ static const WCHAR * const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] =
+ {
+ W(""),
+ W("unmanaged cdecl "),
+ W("unmanaged stdcall "),
+ W("unmanaged thiscall "),
+ W("unmanaged fastcall "),
+ W("vararg "),
+ W("<error> "),
+ W("<error> "),
+ W(""),
+ W(""),
+ W(""),
+ W("native vararg ")
+ };
+
+ if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
+ {
+ appendStrW(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
+ }
+
+
+ numArgs = CorSigUncompressData(typePtr);
+ // do return type
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+
+ }
+ else
+ {
+ numArgs = CorSigUncompressData(typePtr);
+ }
+
+ if (name != 0 && *name != 0)
+ {
+ appendStrW(out, W(" "));
+ appendStrW(out, name);
+ }
+ appendStrW(out, W("("));
+
+ bool needComma = false;
+
+ while (numArgs)
+ {
+ if (typePtr >= typeEnd)
+ break;
+
+ if (*typePtr == ELEMENT_TYPE_SENTINEL)
+ {
+ if (needComma)
+ appendStrW(out, W(","));
+ appendStrW(out, W("..."));
+ typePtr++;
+ }
+ else
+ {
+ if (numArgs <= 0)
+ break;
+ if (needComma)
+ appendStrW(out, W(","));
+ typePtr = PrettyPrintType(typePtr, (typeEnd - typePtr), out, pIMDI);
+ --numArgs;
+ }
+ needComma = true;
+ }
+ appendStrW(out, W(")"));
+ return (asStringW(out));
+} // LPCWSTR PrettyPrintSigWorker()
+
+
+// Internal implementation of PrettyPrintSig().
+
+HRESULT PrettyPrintSigWorkerInternal(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI); // Import api to use.
+
+static HRESULT PrettyPrintClass(
+ PCCOR_SIGNATURE &typePtr, // type to convert
+ PCCOR_SIGNATURE typeEnd, // end of the signature.
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI); // ptr to IMDInternal class with ComSig
+
+
+#ifdef _PREFAST_
+#pragma warning(push)
+#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
+#endif
+//*****************************************************************************
+//*****************************************************************************
+// pretty prints 'type' to the buffer 'out' returns a pointer to the next type,
+// or 0 on a format failure
+
+__checkReturn
+static HRESULT PrettyPrintTypeA(
+ PCCOR_SIGNATURE &typePtr, // type to convert,
+ size_t typeLen, // Maximum length of the type.
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI) // ptr to IMDInternal class with ComSig
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ mdToken tk; // A type's token.
+ const CHAR *str; // Temporary string.
+ HRESULT hr; // A result.
+
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen; // End of the signature.
+ unsigned __int8 elt = *typePtr++;
+
+ switch(elt) {
+ case ELEMENT_TYPE_VOID:
+ str = "void";
+ goto APPEND;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ str = "bool";
+ goto APPEND;
+
+ case ELEMENT_TYPE_CHAR:
+ str = "wchar";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I1:
+ str = "int8";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U1:
+ str = "unsigned int8";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I2:
+ str = "int16";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U2:
+ str = "unsigned int16";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I4:
+ str = "int32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U4:
+ str = "unsigned int32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I8:
+ str = "int64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U8:
+ str = "unsigned int64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_R4:
+ str = "float32";
+ goto APPEND;
+
+ case ELEMENT_TYPE_R8:
+ str = "float64";
+ goto APPEND;
+
+ case ELEMENT_TYPE_U:
+ str = "unsigned int";
+ goto APPEND;
+
+ case ELEMENT_TYPE_I:
+ str = "int";
+ goto APPEND;
+
+ case ELEMENT_TYPE_OBJECT:
+ str = "class System.Object";
+ goto APPEND;
+
+ case ELEMENT_TYPE_STRING:
+ str = "class System.String";
+ goto APPEND;
+
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ str = "class System.__Canon";
+ goto APPEND;
+
+ case ELEMENT_TYPE_TYPEDBYREF:
+ str = "refany";
+ goto APPEND;
+
+ APPEND:
+ IfFailGo(appendStrA(out, str));
+ break;
+
+ case ELEMENT_TYPE_INTERNAL:
+ void* pMT;
+ pMT = *((void* UNALIGNED *)typePtr);
+ typePtr += sizeof(void*);
+ CHAR tempBuffer[64];
+ sprintf_s(tempBuffer, 64, "pMT: %p", pMT);
+ IfFailGo(appendStrA(out, tempBuffer));
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE:
+ str = "value class ";
+ goto DO_CLASS;
+
+ case ELEMENT_TYPE_CLASS:
+ str = "class ";
+ goto DO_CLASS;
+
+ DO_CLASS:
+ IfFailGo(appendStrA(out, str));
+ IfFailGo(PrettyPrintClass(typePtr, typeEnd, out, pIMDI));
+ break;
+
+ case ELEMENT_TYPE_CMOD_REQD:
+ str = "required_modifier ";
+ goto CMOD;
+
+ case ELEMENT_TYPE_CMOD_OPT:
+ str = "optional_modifier ";
+ goto CMOD;
+
+ CMOD:
+ IfFailGo(appendStrA(out, str));
+ IfFailGo(PrettyPrintClass(typePtr, typeEnd, out, pIMDI));
+ IfFailGo(appendStrA(out, " "));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ break;
+
+ case ELEMENT_TYPE_SZARRAY:
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ IfFailGo(appendStrA(out, "[]"));
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ unsigned rank = CorSigUncompressData(typePtr);
+ PREFIX_ASSUME(rank <= 0xffffff);
+ // <TODO>TODO what is the syntax for the rank 0 case? </TODO>
+ if (rank == 0)
+ {
+ IfFailGo(appendStrA(out, "[??]"));
+ }
+ else
+ {
+ _ASSERTE(rank != 0);
+ int* lowerBounds = (int*) _alloca(sizeof(int)*2*rank);
+ int* sizes = &lowerBounds[rank];
+ memset(lowerBounds, 0, sizeof(int)*2*rank);
+
+ unsigned numSizes = CorSigUncompressData(typePtr);
+ _ASSERTE(numSizes <= rank);
+ unsigned int i;
+ for(i =0; i < numSizes; i++)
+ sizes[i] = CorSigUncompressData(typePtr);
+
+ unsigned numLowBounds = CorSigUncompressData(typePtr);
+ _ASSERTE(numLowBounds <= rank);
+ for(i = 0; i < numLowBounds; i++)
+ lowerBounds[i] = CorSigUncompressData(typePtr);
+
+ IfFailGo(appendStrA(out, "["));
+ for(i = 0; i < rank; i++)
+ {
+ if (sizes[i] != 0 && lowerBounds[i] != 0)
+ {
+ appendStrNumA(out, lowerBounds[i]);
+ IfFailGo(appendStrA(out, "..."));
+ appendStrNumA(out, lowerBounds[i] + sizes[i] + 1);
+ }
+ if (i < rank-1)
+ IfFailGo(appendStrA(out, ","));
+ }
+ IfFailGo(appendStrA(out, "]"));
+ }
+ }
+ break;
+
+ case ELEMENT_TYPE_MVAR:
+ IfFailGo(appendStrA(out, "!!"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_VAR:
+ IfFailGo(appendStrA(out, "!"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ unsigned ntypars = CorSigUncompressData(typePtr);
+ IfFailGo(appendStrA(out, "<"));
+ for (unsigned i = 0; i < ntypars; i++)
+ {
+ if (i > 0)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ }
+ IfFailGo(appendStrA(out, ">"));
+ }
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ IfFailGo(appendStrA(out, "[module#"));
+ appendStrNumA(out, CorSigUncompressData(typePtr));
+ IfFailGo(appendStrA(out, ", token#"));
+ typePtr += CorSigUncompressToken(typePtr, &tk);
+ IfFailGo(appendStrHexA(out, tk));
+ IfFailGo(appendStrA(out, "]"));
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ IfFailGo(appendStrA(out, "fnptr "));
+ IfFailGo(PrettyPrintSigWorkerInternal(typePtr, (typeEnd - typePtr), "", out,pIMDI));
+ break;
+
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ IfFailGo(appendStrA(out, "native "));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ break;
+
+ // Modifiers or dependent types
+ case ELEMENT_TYPE_PINNED:
+ str = " pinned";
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_PTR:
+ str = "*";
+ goto MODIFIER;
+
+ case ELEMENT_TYPE_BYREF:
+ str = "&";
+ goto MODIFIER;
+
+ MODIFIER:
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ IfFailGo(appendStrA(out, str));
+ break;
+
+ default:
+ case ELEMENT_TYPE_SENTINEL:
+ case ELEMENT_TYPE_END:
+ hr = E_INVALIDARG;
+ break;
+ }
+ ErrExit:
+ return hr;
+} // PrettyPrintTypeA
+#ifdef _PREFAST_
+#pragma warning(pop)
+#endif
+
+// pretty prints the class 'type' to the buffer 'out'
+static HRESULT PrettyPrintClass(
+ PCCOR_SIGNATURE &typePtr, // type to convert
+ PCCOR_SIGNATURE typeEnd, // end of the signature.
+ CQuickBytes *out, // where to put the pretty printed string
+ IMDInternalImport *pIMDI) // ptr to IMDInternal class with ComSig
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ mdToken tk;
+ const CHAR *str; // type's token.
+ LPCUTF8 pNS; // type's namespace.
+ LPCUTF8 pN; // type's name.
+ HRESULT hr; // result
+
+ IfFailGo(CorSigUncompressToken_EndPtr(typePtr, typeEnd, &tk));
+ str = "<UNKNOWN>";
+
+ if (TypeFromToken(tk) == mdtTypeSpec)
+ {
+ ULONG cSig;
+ PCCOR_SIGNATURE sig;
+ IfFailGo(pIMDI->GetSigFromToken(tk, &cSig, &sig));
+ IfFailGo(PrettyPrintTypeA(sig, cSig, out, pIMDI));
+ }
+ else
+ {
+ if (TypeFromToken(tk) == mdtTypeRef)
+ {
+ //<TODO>@consider: assembly name?</TODO>
+ if (FAILED(pIMDI->GetNameOfTypeRef(tk, &pNS, &pN)))
+ {
+ pNS = pN = "Invalid TypeRef record";
+ }
+ }
+ else
+ {
+ _ASSERTE(TypeFromToken(tk) == mdtTypeDef);
+ if (FAILED(pIMDI->GetNameOfTypeDef(tk, &pN, &pNS)))
+ {
+ pNS = pN = "Invalid TypeDef record";
+ }
+ }
+
+ if (pNS && *pNS)
+ {
+ IfFailGo(appendStrA(out, pNS));
+ IfFailGo(appendStrA(out, NAMESPACE_SEPARATOR_STR));
+ }
+ IfFailGo(appendStrA(out, pN));
+ }
+ return S_OK;
+
+ErrExit:
+ return hr;
+} // static HRESULT PrettyPrintClass()
+
+//*****************************************************************************
+// Converts a com signature to a text signature.
+//
+// Note that this function DOES NULL terminate the result signature string.
+//*****************************************************************************
+HRESULT PrettyPrintSigInternalLegacy(
+ PCCOR_SIGNATURE typePtr, // type to convert,
+ unsigned typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI) // Import api to use.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ return PrettyPrintSigWorkerInternal(typePtr, typeLen, name, out, pIMDI);
+} // HRESULT PrettyPrintSigInternalLegacy()
+
+HRESULT PrettyPrintSigWorkerInternal(
+ PCCOR_SIGNATURE & typePtr, // type to convert,
+ size_t typeLen, // length of type
+ const CHAR * name, // can be "", the name of the method for this sig
+ CQuickBytes * out, // where to put the pretty printed string
+ IMDInternalImport * pIMDI) // Import api to use.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ unsigned numArgs; // Count of arugments to function, or count of local vars.
+ unsigned numTyArgs = 0;
+ PCCOR_SIGNATURE typeEnd = typePtr + typeLen;
+ bool needComma = false;
+
+ out->Shrink(0);
+
+ if (name != 0) // 0 means a local var sig
+ {
+ // get the calling convention out
+ unsigned callConv = CorSigUncompressData(typePtr);
+
+ if (isCallConv(callConv, IMAGE_CEE_CS_CALLCONV_FIELD))
+ {
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ if (name != 0 && *name != 0)
+
+ {
+ IfFailGo(appendStrA(out, " "));
+ IfFailGo(appendStrA(out, name));
+ }
+ goto ErrExit;
+ }
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)
+ IfFailGo(appendStrA(out, "instance "));
+
+ if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ IfFailGo(appendStrA(out, "generic "));
+ numTyArgs = CorSigUncompressData(typePtr);
+ }
+
+ static const CHAR* const callConvNames[IMAGE_CEE_CS_CALLCONV_MAX] =
+ {
+ "",
+ "unmanaged cdecl ",
+ "unmanaged stdcall ",
+ "unmanaged thiscall ",
+ "unmanaged fastcall ",
+ "vararg ",
+ "<error> ",
+ "<error> ",
+ "",
+ "",
+ "",
+ "native vararg "
+ };
+
+ if ((callConv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX)
+ {
+ appendStrA(out, callConvNames[callConv & IMAGE_CEE_CS_CALLCONV_MASK]);
+ }
+
+ numArgs = CorSigUncompressData(typePtr);
+ // do return type
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ }
+ else
+ {
+ numArgs = CorSigUncompressData(typePtr);
+ }
+
+ if (name != 0 && *name != 0)
+ {
+ IfFailGo(appendStrA(out, " "));
+ IfFailGo(appendStrA(out, name));
+ }
+ IfFailGo(appendStrA(out, "("));
+
+ while (numArgs)
+ {
+ if (typePtr >= typeEnd)
+ break;
+
+ if (*typePtr == ELEMENT_TYPE_SENTINEL)
+ {
+ if (needComma)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(appendStrA(out, "..."));
+ ++typePtr;
+ }
+ else
+ {
+ if (needComma)
+ IfFailGo(appendStrA(out, ","));
+ IfFailGo(PrettyPrintTypeA(typePtr, (typeEnd - typePtr), out, pIMDI));
+ --numArgs;
+ }
+ needComma = true;
+ }
+ IfFailGo(appendStrA(out, ")"));
+ if (asStringA(out) == 0)
+ IfFailGo(E_OUTOFMEMORY);
+
+ ErrExit:
+ return hr;
+} // HRESULT PrettyPrintSigWorkerInternal()
diff --git a/src/coreclr/utilcode/regutil.cpp b/src/coreclr/utilcode/regutil.cpp
new file mode 100644
index 00000000000..fcfbe6eb7c7
--- /dev/null
+++ b/src/coreclr/utilcode/regutil.cpp
@@ -0,0 +1,759 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// regutil.cpp
+//
+
+//
+// This module contains a set of functions that can be used to access the
+// registry.
+//
+//*****************************************************************************
+
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "mscoree.h"
+#include "sstring.h"
+#include "ex.h"
+
+#define COMPLUS_PREFIX W("COMPlus_")
+#define LEN_OF_COMPLUS_PREFIX 8
+
+#if (!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(DEBUG)) && !defined(TARGET_UNIX)
+#define ALLOW_REGISTRY
+#endif
+
+#undef WszRegCreateKeyEx
+#undef WszRegOpenKeyEx
+#undef WszRegOpenKey
+#define WszRegCreateKeyEx RegCreateKeyExW
+#define WszRegOpenKeyEx RegOpenKeyExW
+#define WszRegOpenKey(hKey, wszSubKey, phkRes) RegOpenKeyExW(hKey, wszSubKey, 0, KEY_ALL_ACCESS, phkRes)
+
+//*****************************************************************************
+// Reads from the environment setting
+//*****************************************************************************
+LPWSTR REGUTIL::EnvGetString(LPCWSTR name, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ WCHAR buff[64];
+
+ if(wcslen(name) > (size_t)(64 - 1 - (fPrependCOMPLUS ? LEN_OF_COMPLUS_PREFIX : 0)))
+ {
+ return NULL;
+ }
+
+ if (fPrependCOMPLUS)
+ {
+#ifdef ALLOW_REGISTRY
+ if (!EnvCacheValueNameSeenPerhaps(name))
+ return NULL;
+#endif // ALLOW_REGISTRY
+ wcscpy_s(buff, _countof(buff), COMPLUS_PREFIX);
+ }
+ else
+ {
+ *buff = 0;
+ }
+
+ wcscat_s(buff, _countof(buff), name);
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+
+ NewArrayHolder<WCHAR> ret = NULL;
+ HRESULT hr = S_OK;
+ DWORD Len;
+ EX_TRY
+ {
+ PathString temp;
+
+ Len = WszGetEnvironmentVariable(buff, temp);
+ if (Len != 0)
+ {
+ ret = temp.GetCopyOfUnicodeString();
+ }
+
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ }
+
+ if(ret != NULL)
+ {
+ return ret.Extract();
+ }
+
+ return NULL;
+
+
+}
+
+//*****************************************************************************
+// Reads a DWORD from the COR configuration according to the level specified
+// Returns back defValue if the key cannot be found
+//*****************************************************************************
+DWORD REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG result;
+ GetConfigInteger(name, defValue, &result, TRUE, level, fPrependCOMPLUS);
+
+ return (DWORD)result;
+}
+
+#define uniwcst(val, endptr, base) (fGetDWORD ? wcstoul(val, endptr, base) : _wcstoui64(val, endptr, base))
+
+//
+// Look up a dword config value, and write the result to the DWORD passed in by reference.
+//
+// Return value:
+// * E_FAIL if the value is not found. (result is assigned the default value)
+// * S_OK if the value is found. (result is assigned the value that was found)
+//
+// Arguments:
+// * info - see file:../inc/CLRConfig.h for details
+// * result - Pointer to the output DWORD.
+//
+// static
+HRESULT REGUTIL::GetConfigDWORD_DontUse_(LPCWSTR name, DWORD defValue, __out DWORD * result, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ ULONGLONG ullResult;
+ HRESULT hr = GetConfigInteger(name, defValue, &ullResult, TRUE, level, fPrependCOMPLUS);
+ *result = (DWORD)ullResult;
+ return hr;
+}
+
+ULONGLONG REGUTIL::GetConfigULONGLONG_DontUse_(LPCWSTR name, ULONGLONG defValue, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG result;
+ GetConfigInteger(name, defValue, &result, FALSE, level, fPrependCOMPLUS);
+
+ return result;
+}
+
+// This function should really be refactored to return the string from the environment and let the caller decide
+// what to convert it to; and return the buffer read from the reg call.
+// Note for PAL: right now PAL does not have a _wcstoui64 API, so I am temporarily reading in all numbers as
+// a 32-bit number. When we have the _wcstoui64 API on MAC we will use uniwcst instead of wcstoul.
+HRESULT REGUTIL::GetConfigInteger(LPCWSTR name, ULONGLONG defValue, __out ULONGLONG * result, BOOL fGetDWORD, CORConfigLevel level, BOOL fPrependCOMPLUS)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ SUPPORTS_DAC_HOST_ONLY;
+
+ ULONGLONG rtn;
+ ULONGLONG ret = 0;
+ DWORD type = 0;
+ DWORD size = 4;
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ if (level & COR_CONFIG_ENV)
+ {
+ WCHAR* val = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
+ if (val != 0) {
+ errno = 0;
+ LPWSTR endPtr;
+ rtn = uniwcst(val, &endPtr, 16); // treat it has hex
+ BOOL fSuccess = ((errno != ERANGE) && (endPtr != val));
+ delete[] val;
+
+ if (fSuccess) // success
+ {
+ *result = rtn;
+ return (S_OK);
+ }
+ }
+ }
+
+ // Early out if no registry access, simplifies following code.
+ //
+ if (!(level & COR_CONFIG_REGISTRY))
+ {
+ *result = defValue;
+ return (E_FAIL);
+ }
+
+#ifdef ALLOW_REGISTRY
+ // Probe the config cache to see if there is any point
+ // probing the registry; if not, don't bother.
+ //
+ if (!RegCacheValueNameSeenPerhaps(name))
+ {
+ *result = defValue;
+ return (E_FAIL);
+ }
+#endif // ALLOW_REGISTRY
+
+ if (level & COR_CONFIG_USER)
+ {
+#ifdef ALLOW_REGISTRY
+ {
+ LONG retVal = ERROR_SUCCESS;
+ BOOL bCloseHandle = FALSE;
+ HKEY userKey = s_hUserFrameworkKey;
+
+ if (userKey == INVALID_HANDLE_VALUE)
+ {
+ retVal = WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey);
+ bCloseHandle = TRUE;
+ }
+
+ if (retVal == ERROR_SUCCESS)
+ {
+ rtn = WszRegQueryValueEx(userKey, name, 0, &type, (LPBYTE)&ret, &size);
+
+ if (bCloseHandle)
+ VERIFY(!RegCloseKey(userKey));
+
+ if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
+ {
+ *result = ret;
+ return (S_OK);
+ }
+ }
+ }
+#endif // ALLOW_REGISTRY
+ }
+
+ if (level & COR_CONFIG_MACHINE)
+ {
+#ifdef ALLOW_REGISTRY
+ {
+ LONG retVal = ERROR_SUCCESS;
+ BOOL bCloseHandle = FALSE;
+ HKEY machineKey = s_hMachineFrameworkKey;
+
+ if (machineKey == INVALID_HANDLE_VALUE)
+ {
+ retVal = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey);
+ bCloseHandle = TRUE;
+ }
+
+ if (retVal == ERROR_SUCCESS)
+ {
+ rtn = WszRegQueryValueEx(machineKey, name, 0, &type, (LPBYTE)&ret, &size);
+
+ if (bCloseHandle)
+ VERIFY(!RegCloseKey(machineKey));
+
+ if (rtn == ERROR_SUCCESS && (type == REG_DWORD || (!fGetDWORD && type == REG_QWORD)))
+ {
+ *result = ret;
+ return (S_OK);
+ }
+ }
+ }
+#endif // ALLOW_REGISTRY
+ }
+
+ *result = defValue;
+ return (E_FAIL);
+}
+
+//*****************************************************************************
+// Reads a string from the COR configuration according to the level specified
+// The caller is responsible for deallocating the returned string by
+// calling code:REGUTIL::FreeConfigString or using a code:ConfigStringHolder
+//*****************************************************************************
+
+LPWSTR REGUTIL::GetConfigString_DontUse_(LPCWSTR name, BOOL fPrependCOMPLUS, CORConfigLevel level, BOOL fUsePerfCache)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+#ifdef ALLOW_REGISTRY
+ HRESULT lResult;
+ RegKeyHolder userKey = NULL;
+ RegKeyHolder machineKey = NULL;
+ RegKeyHolder fusionKey = NULL;
+ DWORD type;
+ DWORD size;
+#endif // ALLOW_REGISTRY
+ NewArrayHolder<WCHAR> ret(NULL);
+
+ FAULT_NOT_FATAL(); // We don't report OOM errors here, we return a default value.
+
+ if (level & COR_CONFIG_ENV)
+ {
+ ret = EnvGetString(name, fPrependCOMPLUS); // try getting it from the environement first
+ if (ret != 0) {
+ if (*ret != 0)
+ {
+ ret.SuppressRelease();
+ return(ret);
+ }
+ ret.Clear();
+ }
+ }
+
+ // Early out if no registry access, simplifies following code.
+ //
+ if (!(level & COR_CONFIG_REGISTRY))
+ {
+ return(ret);
+ }
+
+#ifdef ALLOW_REGISTRY
+ // Probe the config cache to see if there is any point
+ // probing the registry; if not, don't bother.
+ //
+ if (fUsePerfCache && !RegCacheValueNameSeenPerhaps(name))
+ return ret;
+#endif // ALLOW_REGISTRY
+
+ if (level & COR_CONFIG_USER)
+ {
+#ifdef ALLOW_REGISTRY
+ BOOL bUsingCachedKey = FALSE;
+
+ if (s_hUserFrameworkKey != INVALID_HANDLE_VALUE)
+ {
+ bUsingCachedKey = TRUE;
+ userKey = s_hUserFrameworkKey;
+ }
+
+ if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_CURRENT_USER, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &userKey) == ERROR_SUCCESS)
+ {
+ BOOL bReturn = FALSE;
+ if (WszRegQueryValueEx(userKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
+ type == REG_SZ)
+ {
+ ret = (LPWSTR) new (nothrow) BYTE [size];
+ if (ret)
+ {
+ ret[0] = W('\0');
+ lResult = WszRegQueryValueEx(userKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ {
+ ret.SuppressRelease();
+ }
+ }
+ bReturn = TRUE;
+ }
+
+ if (bUsingCachedKey)
+ userKey.SuppressRelease();
+
+ if (bReturn)
+ return ret;
+ }
+
+#endif // ALLOW_REGISTRY
+ }
+
+ if (level & COR_CONFIG_MACHINE)
+ {
+#ifdef ALLOW_REGISTRY
+ BOOL bUsingCachedKey = FALSE;
+
+ if (s_hMachineFrameworkKey != INVALID_HANDLE_VALUE)
+ {
+ bUsingCachedKey = TRUE;
+ machineKey = s_hMachineFrameworkKey;
+ }
+
+ if (bUsingCachedKey || WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &machineKey) == ERROR_SUCCESS)
+ {
+ BOOL bReturn = FALSE;
+ if (WszRegQueryValueEx(machineKey, name, 0, &type, 0, &size) == ERROR_SUCCESS &&
+ type == REG_SZ)
+ {
+ ret = (LPWSTR) new (nothrow) BYTE [size];
+ if (ret)
+ {
+ ret[0] = W('\0');
+ lResult = WszRegQueryValueEx(machineKey, name, 0, 0, (LPBYTE) ret.GetValue(), &size);
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ {
+ ret.SuppressRelease();
+ }
+ }
+ bReturn = TRUE;
+ }
+
+ if (bUsingCachedKey)
+ machineKey.SuppressRelease();
+
+ if (bReturn)
+ return ret;
+ }
+
+#endif // ALLOW_REGISTRY
+ }
+
+ return NULL;
+}
+
+//*****************************************************************************
+// Deallocation function for code:REGUTIL::GetConfigString_DontUse_
+//
+// Notes:
+// Use a code:ConfigStringHolder to automatically call this.
+//*****************************************************************************
+void REGUTIL::FreeConfigString(__in_z LPWSTR str)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ delete [] str;
+}
+
+//*****************************************************************************
+// Reads a BIT flag from the COR configuration according to the level specified
+// Returns back defValue if the key cannot be found
+//*****************************************************************************
+DWORD REGUTIL::GetConfigFlag_DontUse_(LPCWSTR name, DWORD bitToSet, BOOL defValue)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return(GetConfigDWORD_DontUse_(name, defValue) != 0 ? bitToSet : 0);
+}
+
+
+#ifdef ALLOW_REGISTRY
+
+
+
+//
+// ProbabilisticNameSet:
+//
+// (Used by ConfigCache, below. If used elsewhere, might justify
+// promotion to a standalone header file.)
+//
+// Represent a set of names in a small, fixed amount of storage.
+// We turn a name into a small integer, then add the integer to a bitvector.
+// An old trick we used in VC++4 minimal rebuild.
+//
+// For best results, the number of elements should be a fraction of
+// the total number of bits in 'bits'.
+//
+// Note, only the const methods are thread-safe.
+// Callers are responsible for providing their own synchronization when
+// constructing and Add'ing names to the set.
+//
+class ProbabilisticNameSet {
+public:
+ ProbabilisticNameSet()
+ {
+ WRAPPER_NO_CONTRACT;
+
+ memset(bits, 0, sizeof(bits));
+ }
+
+ // Add a name to the set.
+ //
+ void Add(LPCWSTR name)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, 0, &i, &mask);
+ bits[i] |= mask;
+ }
+
+ void Add(LPCWSTR name, DWORD count)
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, count, &i, &mask);
+ bits[i] |= mask;
+ }
+
+ // Return TRUE if a name *may have* been added to the set;
+ // return FALSE if the name *definitely* was NOT ever added to the set.
+ //
+ BOOL MayContain(LPCWSTR name) const
+ {
+ WRAPPER_NO_CONTRACT;
+
+ unsigned i, mask;
+ GetBitIndex(name, 0, &i, &mask);
+ return !!(bits[i] & mask);
+ }
+
+private:
+ static const unsigned cbitSet = 256U;
+ static const unsigned cbitWord = 8U*sizeof(unsigned);
+ unsigned bits[cbitSet/cbitWord];
+
+ // Return the word index and bit mask corresponding to the bitvector member
+ // addressed by the *case-insensitive* hash of the given name.
+ //
+ void GetBitIndex(LPCWSTR name, DWORD count, unsigned* pi, unsigned* pmask) const
+ {
+ LIMITED_METHOD_CONTRACT;
+ unsigned hash;
+ if (count > 0)
+ hash = HashiStringNKnownLower80(name, count) % cbitSet;
+ else
+ hash = HashiStringKnownLower80(name) % cbitSet;
+ *pi = hash / cbitWord;
+ *pmask = (1U << (hash % cbitWord));
+ }
+
+};
+
+
+// From the Win32 SDK docs:
+// Registry Element Size Limits
+// ...
+// The maximum size of a value name is as follows:
+// Windows Server 2003 and Windows XP: 16,383 characters
+// Windows 2000: 260 ANSI characters or 16,383 Unicode characters.
+// Windows Me/98/95: 255 characters
+// Despite that, we only cache value names of 80 characters or less --
+// longer names don't make sense as configuration settings names.
+//
+static const unsigned cchRegValueNameMax = 80;
+
+BOOL REGUTIL::s_fUseRegCache = FALSE;
+BOOL REGUTIL::s_fUseEnvCache = FALSE;
+HKEY REGUTIL::s_hMachineFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
+HKEY REGUTIL::s_hUserFrameworkKey = (HKEY) INVALID_HANDLE_VALUE;
+static ProbabilisticNameSet regNames; // set of registry value names seen; should be
+ // a static field of REGUTIL but I don't
+ // want to expose ProbabilisticNameSet.
+static ProbabilisticNameSet envNames; // set of environment value names seen;
+
+// "Registry Configuration Cache"
+//
+// Initialize the (optional) registry config cache.
+//
+// The purpose of the cache is to avoid hundreds of registry probes
+// otherwise incurred by calls to GetConfigDWORD_DontUse_ and GetConfigString_DontUse_.
+//
+// We accomplish this by enumerating the relevant registry keys and
+// remembering the extant value names; and then by avoiding probing
+// for a name that was not seen in the enumeration (initialization) phase.
+//
+// It is optional in the sense that REGUTIL facilities like
+// GetConfigDWORD_DontUse_ and GetConfigString_DontUse_ will work fine if the cache
+// is never initialized; however, each config access then will hit
+// the registry (typically multiple times to search HKCU and HKLM).
+//
+//
+// Initialization: Enumerate these registry keys
+// HKCU Software\Microsoft\.NetFramework
+// HKLM Software\Microsoft\.NetFramework
+// for value names, and "remember" them in the ProbalisticNameSet 'names'.
+//
+// If we ever find a reg value named DisableConfigCache under any of these
+// three keys, the feature is disabled.
+//
+// This method is not thread-safe. It should only be called once.
+//
+// Perf Optimization for VSWhidbey:113373.
+//
+void REGUTIL::InitOptionalConfigCache()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ static const HKEY roots[] = { HKEY_CURRENT_USER,
+ HKEY_LOCAL_MACHINE};
+
+ LONG l = ERROR_SUCCESS; // general Win32 API error return code
+ HKEY hkey = NULL;
+
+ // No caching if the environment variable COMPlus_DisableConfigCache is set
+ //
+ if (CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_DisableConfigCache) != 0)
+ goto failure;
+
+ // Enumerate each root
+ //
+ for (int i = 0; i < NumItems(roots); i++) {
+ hkey = NULL; // defensive
+ l = WszRegOpenKeyEx(roots[i], FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hkey);
+ if (l == ERROR_FILE_NOT_FOUND) {
+ // That registry key is not present.
+ // For example, installation with no HKCU\...\.NETFramework.
+ // Should be OK to proceed.
+ continue;
+ }
+ else if (l == ERROR_ACCESS_DENIED) {
+ // If we encounter access denied for the current key, ignore
+ // the failure and continue to cache the rest. Effectively this means
+ // we are caching that key as containing no values, which is correct
+ // because in the unlikely event there are values hiding underneath
+ // later attempts to access them (open the key) would also hit access
+ // denied and continue on probing other locations.
+ continue;
+ }
+ else if (l != ERROR_SUCCESS) {
+ // Something else went wrong. To be safe, don't enable the cache.
+ goto failure;
+ }
+
+ // Enumerate every value name under this key.
+ //
+ for (int j = 0; ; j++) {
+ WCHAR wszValue[cchRegValueNameMax + 1];
+ DWORD dwValueSize = NumItems(wszValue);
+ l = WszRegEnumValue(hkey, j, wszValue, &dwValueSize,
+ NULL, NULL, NULL, NULL);
+
+ if (l == ERROR_SUCCESS) {
+ // Add value name to the names cache.
+ regNames.Add(wszValue);
+ }
+ else if (l == ERROR_NO_MORE_ITEMS) {
+ // Expected case: we've considered every value under this key.
+ break;
+ }
+ else if ((l == ERROR_INSUFFICIENT_BUFFER || l == ERROR_MORE_DATA) &&
+ (dwValueSize > cchRegValueNameMax)) {
+ // Name is too long. That's OK, we don't cache such names.
+ continue;
+ }
+ else if (l == ERROR_ACCESS_DENIED) {
+ // As above, ignore access denied and continue on trying to cache
+ continue;
+ }
+ else {
+ // WszRegEnumValue failed OOM, or something else went wrong.
+ // To be safe, don't enable the cache.
+ goto failure;
+ }
+ }
+
+ // Save the handles to framework regkeys so that future reads dont have to
+ // open it again
+ if (roots[i] == HKEY_CURRENT_USER)
+ s_hUserFrameworkKey = hkey;
+ else if (roots[i] == HKEY_LOCAL_MACHINE)
+ s_hMachineFrameworkKey = hkey;
+ else
+ RegCloseKey(hkey);
+
+ hkey = NULL;
+ }
+
+ // Success. We've enumerated all value names under the roots;
+ // enable the REGUTIL value name config cache.
+ //
+ s_fUseRegCache = TRUE;
+
+ // Now create a cache of environment variables
+ if (WCHAR * wszStrings = WszGetEnvironmentStrings())
+ {
+ // GetEnvironmentStrings returns pointer to a null terminated block containing
+ // null terminated strings
+ for(WCHAR *wszCurr = wszStrings; *wszCurr; wszCurr++)
+ {
+ WCHAR wch = towlower(*wszCurr);
+
+ // Lets only cache env variables with the COMPlus prefix only
+ if (wch == W('c'))
+ {
+ WCHAR *wszName = wszCurr;
+
+ // Look for the separator between name and value
+ while (*wszCurr && *wszCurr != W('='))
+ wszCurr++;
+
+ if (*wszCurr == W('='))
+ {
+ // Check the prefix
+ if(!SString::_wcsnicmp(wszName, COMPLUS_PREFIX, LEN_OF_COMPLUS_PREFIX))
+ {
+ wszName += LEN_OF_COMPLUS_PREFIX;
+ envNames.Add(wszName, (DWORD) (wszCurr - wszName));
+ }
+ }
+
+ }
+ // Look for current string termination
+ while (*wszCurr)
+ wszCurr++;
+
+ }
+
+ WszFreeEnvironmentStrings(wszStrings);
+ s_fUseEnvCache = TRUE;
+
+ }
+ return;
+
+failure:
+ if (hkey != NULL)
+ RegCloseKey(hkey);
+}
+
+// Return TRUE if the registry value name was seen (or might have been seen)
+// in the registry at cache initialization time;
+// return FALSE if it definitely was not seen at startup.
+//
+// If not using the config cache, return TRUE always.
+//
+// Perf Optimization for VSWhidbey:113373.
+//
+BOOL REGUTIL::RegCacheValueNameSeenPerhaps(LPCWSTR name)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return !s_fUseRegCache
+ || (wcslen(name) > cchRegValueNameMax)
+ || regNames.MayContain(name);
+}
+
+BOOL REGUTIL::EnvCacheValueNameSeenPerhaps(LPCWSTR name)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return !s_fUseEnvCache
+ || envNames.MayContain(name);
+}
+
+#endif // ALLOW_REGISTRY
diff --git a/src/coreclr/utilcode/safewrap.cpp b/src/coreclr/utilcode/safewrap.cpp
new file mode 100644
index 00000000000..20903862818
--- /dev/null
+++ b/src/coreclr/utilcode/safewrap.cpp
@@ -0,0 +1,336 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// SafeWrap.cpp
+//
+
+//
+// This file contains wrapper functions for Win32 API's that take SStrings
+// and use CLR-safe holders.
+//
+// See guidelines in SafeWrap.h for writing these APIs.
+//*****************************************************************************
+
+#include "stdafx.h" // Precompiled header key.
+#include "safewrap.h"
+#include "winwrap.h" // Header for macros and functions.
+#include "utilcode.h"
+#include "holder.h"
+#include "sstring.h"
+#include "ex.h"
+
+//-----------------------------------------------------------------------------
+// Get the current directory.
+// On success, returns true and sets 'Value' to unicode version of cur dir.
+// Throws on all failures. This should mainly be oom.
+//-----------------------------------------------------------------------------
+void ClrGetCurrentDirectory(SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // Get size needed
+ DWORD len = WszGetCurrentDirectory(value);
+
+
+ // An actual API failure in GetCurrentDirectory failure should be very rare, so we'll throw on those.
+ if (len == 0)
+ {
+ ThrowLastError();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Reads an environment variable into the given SString.
+// Returns true on success, false on failure (includes if the var does not exist).
+// May throw on oom.
+//-----------------------------------------------------------------------------
+bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+
+ PRECONDITION(szEnvVarName != NULL);
+ }
+ CONTRACTL_END;
+
+ // First read it to get the needed length.
+ DWORD lenWithNull = GetEnvironmentVariableA(szEnvVarName, NULL, 0);
+ if (lenWithNull == 0)
+ {
+ return false;
+ }
+
+ // Now read it for content.
+ char * pCharBuf = value.OpenANSIBuffer(lenWithNull);
+ DWORD lenWithoutNull = GetEnvironmentVariableA(szEnvVarName, pCharBuf, lenWithNull);
+ value.CloseBuffer(lenWithoutNull);
+
+ if (lenWithoutNull != (lenWithNull - 1))
+ {
+ // Env var must have changed underneath us.
+ return false;
+ }
+ return true;
+}
+
+void ClrGetModuleFileName(HMODULE hModule, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ WCHAR * pCharBuf = value.OpenUnicodeBuffer(_MAX_PATH);
+ DWORD numChars = GetModuleFileNameW(hModule, pCharBuf, _MAX_PATH);
+ value.CloseBuffer(numChars);
+}
+
+ClrDirectoryEnumerator::ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask /*= W("*")*/)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ StackSString strMask(pBaseDirectory);
+ SString s(SString::Literal, DIRECTORY_SEPARATOR_STR_W);
+ if (!strMask.EndsWith(s))
+ {
+ strMask.Append(s);
+ }
+ strMask.Append(pMask);
+ dirHandle = WszFindFirstFile(strMask, &data);
+
+ if (dirHandle == INVALID_HANDLE_VALUE)
+ {
+ DWORD dwLastError = GetLastError();
+
+ // We either ran out of files, or didnt encounter even a single file matching the
+ // search mask. If it is neither of these conditions, then convert the error to an exception
+ // and raise it.
+ if ((dwLastError != ERROR_FILE_NOT_FOUND) && (dwLastError != ERROR_NO_MORE_FILES))
+ ThrowLastError();
+ }
+
+ fFindNext = FALSE;
+}
+
+bool ClrDirectoryEnumerator::Next()
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ if (dirHandle == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ for (;;)
+ {
+ if (fFindNext)
+ {
+ if (!WszFindNextFile(dirHandle, &data))
+ {
+ if (GetLastError() != ERROR_NO_MORE_FILES)
+ ThrowLastError();
+
+ return FALSE;
+ }
+ }
+ else
+ {
+ fFindNext = TRUE;
+ }
+
+ // Skip junk
+ if (wcscmp(data.cFileName, W(".")) != 0 && wcscmp(data.cFileName, W("..")) != 0)
+ return TRUE;
+ }
+}
+
+DWORD ClrReportEvent(
+ LPCWSTR pEventSource,
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ WORD wNumStrings,
+ LPCWSTR *lpStrings,
+ DWORD dwDataSize /*=0*/,
+ LPVOID lpRawData /*=NULL*/)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#ifndef TARGET_UNIX
+ HANDLE h = ::RegisterEventSourceW(
+ NULL, // uses local computer
+ pEventSource);
+ if (h == NULL)
+ {
+ // Return the error code to the caller so that
+ // appropriate asserts/logging can be done
+ // incase of errors like event log being full
+ return GetLastError();
+ }
+
+ // Every event id should have matching description in dlls\shim\eventmsg.mc. This allows
+ // event view to know how to display message.
+ _ASSERTE (dwEventID != 0);
+
+ // Attempt to report the event to the event log. Note that if the operation fails
+ // (for example because of low memory conditions) it isn't fatal so we can safely ignore
+ // the return code from ReportEventW.
+ BOOL ret = ::ReportEventW(
+ h, // event log handle
+ wType,
+ wCategory,
+ dwEventID,
+ lpUserSid,
+ wNumStrings,
+ dwDataSize,
+ lpStrings,
+ lpRawData);
+
+ DWORD dwRetStatus = GetLastError();
+
+ ::DeregisterEventSource(h);
+
+ return (ret == TRUE)?ERROR_SUCCESS:dwRetStatus;
+#else // TARGET_UNIX
+ // UNIXTODO: Report the event somewhere?
+ return ERROR_SUCCESS;
+#endif // TARGET_UNIX
+}
+
+// Returns ERROR_SUCCESS if succeessful in reporting to event log, or
+// Windows error code to indicate the specific error.
+DWORD ClrReportEvent(
+ LPCWSTR pEventSource,
+ WORD wType,
+ WORD wCategory,
+ DWORD dwEventID,
+ PSID lpUserSid,
+ LPCWSTR pMessage)
+{
+ return ClrReportEvent(pEventSource, wType, wCategory, dwEventID, lpUserSid, 1, &pMessage);
+}
+
+#ifndef TARGET_UNIX
+// Read a REG_SZ (null-terminated string) value from the registry. Throws.
+//
+// Arguments:
+// hKey - key to registry hive.
+// szValueName - null-terminated string for value name to lookup.
+// If this is empty, this gets the (default) value in the registry hive.
+// value - out parameter to hold registry value string contents.
+//
+// Returns:
+// value is set on success. Throws on any failure, including if the value doesn't exist
+// or if the value exists but is not a REG_SZ.
+//
+// Notes:
+// REG_SZ is a single null-terminated string in the registry.
+// This is only support on Windows because the registry is windows specific.
+void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD type;
+ DWORD numBytesData;
+
+ // Preemptively clear the string such that it's empty in any failure case.
+ value.Clear();
+
+ //
+ // Step 1: First call to find size of buffer and ensure data type is correct
+ //
+ LONG ret = WszRegQueryValueEx(
+ hKey,
+ szValueName.GetUnicode(), // NULL or "\0" represents the (default) key.
+ NULL, // reserved
+ &type, // should be REG_SZ
+ NULL, // not requesting data yet
+ &numBytesData
+ );
+
+ if (ret != ERROR_SUCCESS)
+ {
+ ThrowWin32(ret);
+ }
+
+ if (type != REG_SZ)
+ {
+ // The registry value is not a string.
+ ThrowHR(E_INVALIDARG);
+ }
+
+ // REG_SZ includes the null terminator.
+ DWORD numCharsIncludingNull = numBytesData / sizeof(WCHAR);
+
+ //
+ // Step 2: Allocate buffer to hold final result
+ //
+ WCHAR * pData = value.OpenUnicodeBuffer(numCharsIncludingNull);
+ DWORD numBytesData2 = numBytesData;
+
+
+ //
+ // Step 3: Requery to get actual contents
+ //
+ ret = WszRegQueryValueEx(
+ hKey,
+ szValueName.GetUnicode(),
+ NULL, // reserved
+ &type, // should still be REG_SZ
+ (LPBYTE) pData,
+ &numBytesData2
+ );
+
+ // This check should only fail if the registry was changed inbetween the first query
+ // and the second. In practice, that should never actually happen.
+ if ((numBytesData2 != numBytesData) || (type != REG_SZ))
+ {
+ // On error, leave string empty.
+ value.CloseBuffer(0);
+
+ ThrowHR(E_FAIL);
+ }
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // On error, leave string empty.
+ value.CloseBuffer(0);
+ ThrowWin32(ret);
+ }
+
+
+ //
+ // Step 4: Close the string buffer
+ //
+ COUNT_T numCharsNoNull = numCharsIncludingNull - 1;
+ value.CloseBuffer(numCharsNoNull);
+}
+#endif // TARGET_UNIX
diff --git a/src/coreclr/utilcode/sbuffer.cpp b/src/coreclr/utilcode/sbuffer.cpp
new file mode 100644
index 00000000000..215efefb199
--- /dev/null
+++ b/src/coreclr/utilcode/sbuffer.cpp
@@ -0,0 +1,145 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+
+//
+// Buffer.cpp
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sbuffer.h"
+#include "ex.h"
+#include "holder.h"
+#include "stdmacros.h"
+
+// Try to minimize the performance impact of contracts on CHK build.
+#if defined(_MSC_VER)
+#pragma inline_depth (20)
+#endif
+
+const DWORD g_garbageFillBuffer[GARBAGE_FILL_BUFFER_ITEMS] =
+{
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+ GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD, GARBAGE_FILL_DWORD,
+};
+
+//----------------------------------------------------------------------------
+// ReallocateBuffer
+// Low level buffer reallocate routine
+//----------------------------------------------------------------------------
+void SBuffer::ReallocateBuffer(COUNT_T allocation, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckBufferClosed());
+ PRECONDITION(CheckAllocation(allocation));
+ PRECONDITION(allocation >= m_size);
+ POSTCONDITION(m_allocation == allocation);
+ if (allocation > 0) THROWS; else NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ BYTE *newBuffer = NULL;
+ if (allocation > 0)
+ {
+ newBuffer = NewBuffer(allocation);
+
+ if (preserve == PRESERVE)
+ {
+ // Copy the relevant contents of the old buffer over
+ DebugMoveBuffer(newBuffer, m_buffer, m_size);
+ }
+ }
+
+ if (IsAllocated())
+ DeleteBuffer(m_buffer, m_allocation);
+
+ m_buffer = newBuffer;
+ m_allocation = allocation;
+
+ if (allocation > 0)
+ SetAllocated();
+ else
+ ClearAllocated();
+
+ ClearImmutable();
+
+ RETURN;
+}
+
+void SBuffer::Replace(const Iterator &i, COUNT_T deleteSize, COUNT_T insertSize)
+{
+ CONTRACT_VOID
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ PRECONDITION(CheckIteratorRange(i, deleteSize));
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ COUNT_T startRange = (COUNT_T) (i.m_ptr - m_buffer);
+ // The PRECONDITION(CheckIterationRange(i, deleteSize)) should check this in
+ // contract-checking builds, but this ensures we don't integer overflow if someone
+ // passes in an egregious deleteSize by capping it to the remaining length in the
+ // buffer.
+ if ((COUNT_T)(m_buffer + m_size - i.m_ptr) < deleteSize)
+ {
+ _ASSERTE(!"Trying to replace more bytes than are remaining in the buffer.");
+ deleteSize = (COUNT_T)(m_buffer + m_size - i.m_ptr);
+ }
+ COUNT_T endRange = startRange + deleteSize;
+ COUNT_T end = m_size;
+
+ SCOUNT_T delta = insertSize - deleteSize;
+
+ if (delta < 0)
+ {
+ // Buffer is shrinking
+
+ DebugDestructBuffer(i.m_ptr, deleteSize);
+
+ DebugMoveBuffer(m_buffer + endRange + delta,
+ m_buffer + endRange,
+ end - endRange);
+
+ Resize(m_size+delta, PRESERVE);
+
+ i.Resync(this, m_buffer + startRange);
+
+ }
+ else if (delta > 0)
+ {
+ // Buffer is growing
+
+ ResizePadded(m_size+delta);
+
+ i.Resync(this, m_buffer + startRange);
+
+ DebugDestructBuffer(i.m_ptr, deleteSize);
+
+ DebugMoveBuffer(m_buffer + endRange + delta,
+ m_buffer + endRange,
+ end - endRange);
+
+ }
+ else
+ {
+ // Buffer stays the same size. We need to DebugDestruct it first to keep
+ // the invariant that the new space is clean.
+
+ DebugDestructBuffer(i.m_ptr, insertSize);
+ }
+
+ DebugConstructBuffer(i.m_ptr, insertSize);
+
+ RETURN;
+}
+
+
diff --git a/src/coreclr/utilcode/securityutil.cpp b/src/coreclr/utilcode/securityutil.cpp
new file mode 100644
index 00000000000..d3cab8ac1a4
--- /dev/null
+++ b/src/coreclr/utilcode/securityutil.cpp
@@ -0,0 +1,494 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h"
+
+#include "securityutil.h"
+#include "ex.h"
+
+#include "securitywrapper.h"
+
+// These are the right that we will give to the global section and global events used
+// in communicating between debugger and debugee
+//
+// SECTION_ALL_ACCESS is needed for the IPC block. Unfortunately, we DACL our events and
+// IPC block identically. Or this particular right does not need to bleed into here.
+//
+#ifndef CLR_IPC_GENERIC_RIGHT
+#define CLR_IPC_GENERIC_RIGHT (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | STANDARD_RIGHTS_ALL | SECTION_ALL_ACCESS)
+#endif
+
+
+//*****************************************************************
+// static helper function
+//
+// helper to form ACL that contains AllowedACE of users of current
+// process and target process
+//
+// [IN] pid - target process id
+// [OUT] ppACL - ACL for the process
+//
+// Clean up -
+// Caller remember to call FreeACL() on *ppACL
+//*****************************************************************
+HRESULT SecurityUtil::GetACLOfPid(DWORD pid, PACL *ppACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ _ASSERTE(ppACL);
+ *ppACL = NULL;
+
+ PSID pCurrentProcessSid = NULL;
+ PSID pTargetProcessSid = NULL;
+ PSID pTargetProcessAppContainerSid = NULL;
+ DWORD cSid = 0;
+ DWORD dwAclSize = 0;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid on pid : 0x%08x\n",
+ pid));
+
+
+ SidBuffer sidCurrentProcess;
+ SidBuffer sidTargetProcess;
+ SidBuffer sidTargetProcessAppContainer;
+
+ // Get sid for current process.
+ if (SUCCEEDED(sidCurrentProcess.InitFromProcessNoThrow(GetCurrentProcessId())))
+ {
+ pCurrentProcessSid = sidCurrentProcess.GetSid().RawSid();
+ cSid++;
+ }
+
+ // Get sid for target process.
+ if (SUCCEEDED(sidTargetProcess.InitFromProcessNoThrow(pid)))
+ {
+ pTargetProcessSid = sidTargetProcess.GetSid().RawSid();
+ cSid++;
+ }
+
+ //FISHY: what is the scenario where only one of the above calls succeeds?
+ if (cSid == 0)
+ {
+ // failed to get any useful sid. Just return.
+ // need a better error.
+ //
+ hr = E_FAIL;
+ goto exit;
+ }
+
+ hr = sidTargetProcessAppContainer.InitFromProcessAppContainerSidNoThrow(pid);
+ if (FAILED(hr))
+ {
+ goto exit;
+ }
+ else if (hr == S_OK)
+ {
+ pTargetProcessAppContainerSid = sidTargetProcessAppContainer.GetSid().RawSid();
+ cSid++;
+ }
+ else if(hr == S_FALSE) //not an app container, no sid to add
+ {
+ hr = S_OK; // don't leak S_FALSE
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetACLOfPid number of sid : 0x%08x\n",
+ cSid));
+
+ // Now allocate space for ACL. First calculate the space is need to hold ACL
+ dwAclSize = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) * cSid;
+ if (pCurrentProcessSid)
+ {
+ dwAclSize += GetLengthSid(pCurrentProcessSid);
+ }
+ if (pTargetProcessSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessSid);
+ }
+ if (pTargetProcessAppContainerSid)
+ {
+ dwAclSize += GetLengthSid(pTargetProcessAppContainerSid);
+ }
+
+ *ppACL = (PACL) new (nothrow) char[dwAclSize];
+ if (*ppACL == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+
+ // Initialize ACL
+ // add each sid to the allowed ace list
+ if (!InitializeAcl(*ppACL, dwAclSize, ACL_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ if (pCurrentProcessSid)
+ {
+ // add the current process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pCurrentProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessSid)
+ {
+ // add the target process's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ if (pTargetProcessAppContainerSid)
+ {
+ // add the target process's AppContainer's sid into ACL if we have it
+ if (!AddAccessAllowedAce(*ppACL, ACL_REVISION, CLR_IPC_GENERIC_RIGHT, pTargetProcessAppContainerSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ // we better to form a valid ACL to return
+ _ASSERTE(IsValidAcl(*ppACL));
+exit:
+ if (FAILED(hr) && *ppACL)
+ {
+ delete [] (reinterpret_cast<char*>(ppACL));
+ }
+ return hr;
+} // SecurityUtil::GetACLOfPid
+
+
+//*****************************************************************
+// static helper function
+//
+// free the ACL allocated by SecurityUtil::GetACLOfPid
+//
+// [IN] pACL - ACL to be freed
+//
+//*****************************************************************
+void SecurityUtil::FreeACL(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ if (pACL)
+ {
+ delete [] (reinterpret_cast<char*>(pACL));
+ }
+} // SecurityUtil::FreeACL
+
+
+//*****************************************************************
+// constructor
+//
+// [IN] pACL - ACL that this instance of SecurityUtil will held on to
+//
+//*****************************************************************
+SecurityUtil::SecurityUtil(PACL pACL)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ m_pACL = pACL;
+ m_pSacl = NULL;
+ m_fInitialized = false;
+}
+
+//*****************************************************************
+// destructor
+//
+// free the ACL that this instance of SecurityUtil helds on to
+//
+//*****************************************************************
+SecurityUtil::~SecurityUtil()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+ FreeACL(m_pACL);
+ FreeACL(m_pSacl);
+}
+
+//*****************************************************************
+// Initialization function
+//
+// form the SecurityDescriptor that will represent the m_pACL
+//
+//*****************************************************************
+HRESULT SecurityUtil::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ if (m_pACL)
+ {
+ if (!InitializeSecurityDescriptor(&m_SD, SECURITY_DESCRIPTOR_REVISION))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+ if (!SetSecurityDescriptorDacl(&m_SD, TRUE, m_pACL, FALSE))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ return hr;
+ }
+
+ m_SA.nLength = sizeof(SECURITY_ATTRIBUTES);
+ m_SA.lpSecurityDescriptor = &m_SD;
+ m_SA.bInheritHandle = FALSE;
+ m_fInitialized = true;
+ }
+ return S_OK;
+}
+
+// ***************************************************************************
+// Initialization functions which will call the normal Init and add a
+// mandatory label entry to the sacl
+//
+// Expects hProcess to be a valid handle to the process which has the desired
+// mandatory label
+// ***************************************************************************
+HRESULT SecurityUtil::Init(HANDLE hProcess)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = Init();
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ NewArrayHolder<BYTE> pLabel;
+
+ hr = GetMandatoryLabelFromProcess(hProcess, &pLabel);
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ TOKEN_MANDATORY_LABEL * ptml = (TOKEN_MANDATORY_LABEL *) pLabel.GetValue();
+
+ hr = SetSecurityDescriptorMandatoryLabel(ptml->Label.Sid);
+
+ return hr;
+}
+
+
+// ***************************************************************************
+// Given a process, this will put the mandatory label into a buffer and point
+// ppbLabel at the buffer.
+//
+// Caller must free ppbLabel via the array "delete []" operator
+// ***************************************************************************
+HRESULT SecurityUtil::GetMandatoryLabelFromProcess(HANDLE hProcess, LPBYTE * ppbLabel)
+{
+ *ppbLabel = NULL;
+
+ DWORD dwSize = 0;
+ HandleHolder hToken;
+ DWORD err = 0;
+
+ if(!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, NULL, 0, &dwSize))
+ {
+ err = GetLastError();
+ }
+
+ // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
+ // dwSize has the correct buffer size in it.
+ if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
+ {
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
+ if (pLabel == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // Our caller will be freeing the memory so use Extract
+ *ppbLabel = pLabel.Extract();
+
+ return S_OK;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Returns pointer inside the specified mandatory SID to the DWORD representing the
+// integrity level of the process. This DWORD will be one of the
+// SECURITY_MANDATORY_*_RID constants.
+//
+// Arguments:
+// psidIntegrityLevelLabel - [in] PSID in which to find the integrity level.
+//
+// Return Value:
+// Pointer to the RID stored in the specified SID. This RID represents the
+// integrity level of the process
+//
+
+// static
+DWORD * SecurityUtil::GetIntegrityLevelFromMandatorySID(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ return GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
+}
+
+// Creates a mandatory label ace and sets it to be the entry in the
+// security descriptor's sacl. This assumes there are no other entries
+// in the sacl
+HRESULT SecurityUtil::SetSecurityDescriptorMandatoryLabel(PSID psidIntegrityLevelLabel)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD cbSid = GetLengthSid(psidIntegrityLevelLabel);
+ DWORD cbAceStart = offsetof(SYSTEM_MANDATORY_LABEL_ACE, SidStart);
+ // We are about allocate memory for a ACL and an ACE so we need space for:
+ // 1) the ACL: sizeof(ACL)
+ // 2) the entry: the sid is of variable size, so the SYSTEM_MANDATORY_LABEL_ACE
+ // structure has only the first DWORD of the sid in its definition, to get the
+ // appropriate size we get size without SidStart and add on the actual size of the sid
+ DWORD cbSacl = sizeof(ACL) + cbAceStart + cbSid;
+
+ NewArrayHolder<BYTE> sacl = new (nothrow) BYTE[cbSacl];
+
+ m_pSacl = NULL;
+
+ if (sacl == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+ ZeroMemory(sacl.GetValue(), cbSacl);
+ PACL pSacl = reinterpret_cast<ACL *>(sacl.GetValue());
+ SYSTEM_MANDATORY_LABEL_ACE * pLabelAce = reinterpret_cast<SYSTEM_MANDATORY_LABEL_ACE *>(sacl.GetValue() + sizeof(ACL));
+ PSID psid = reinterpret_cast<SID *>(&pLabelAce->SidStart);
+
+ // Our buffer looks like this now: (not drawn to scale)
+ // sacl pSacl pLabelAce psid
+ // - -
+ // | |
+ // | - -
+ // | |
+ // | | -
+ // | - |
+ // - -
+
+ DWORD dwIntegrityLevel = *(GetIntegrityLevelFromMandatorySID(psidIntegrityLevelLabel));
+
+ if (dwIntegrityLevel >= SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ // No need to set the integrity level unless it's lower than medium
+ return S_OK;
+ }
+
+ if(!InitializeAcl(pSacl, cbSacl, ACL_REVISION))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ pSacl->AceCount = 1;
+
+ pLabelAce->Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
+ pLabelAce->Header.AceSize = WORD(cbAceStart + cbSid);
+ pLabelAce->Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
+
+ memcpy(psid, psidIntegrityLevelLabel, cbSid);
+
+ if(!SetSecurityDescriptorSacl(m_SA.lpSecurityDescriptor, TRUE, pSacl, FALSE))
+ {
+ return HRESULT_FROM_GetLastError();
+ }
+
+ // No need to delete the sacl buffer, it will be deleted in the
+ // destructor of this class
+ m_pSacl = (PACL)sacl.Extract();
+ return S_OK;
+}
+
+//*****************************************************************
+// Return SECURITY_ATTRIBUTES that we form in the Init function
+//
+// No clean up is needed after calling this function. The destructor of the
+// instance will do the right thing. Note that this is designed such that
+// we minimize memory allocation, ie the SECURITY_DESCRIPTOR and
+// SECURITY_ATTRIBUTES are embedded in the SecurityUtil instance.
+//
+// Caller should not modify the returned SECURITY_ATTRIBUTES!!!
+//*****************************************************************
+HRESULT SecurityUtil::GetSA(SECURITY_ATTRIBUTES **ppSA)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppSA);
+
+ if (m_fInitialized == false)
+ {
+ _ASSERTE(!"Bad code path!");
+ *ppSA = NULL;
+ return E_FAIL;
+ }
+
+ *ppSA = &m_SA;
+ return S_OK;
+}
diff --git a/src/coreclr/utilcode/securitywrapper.cpp b/src/coreclr/utilcode/securitywrapper.cpp
new file mode 100644
index 00000000000..8f7ac38f444
--- /dev/null
+++ b/src/coreclr/utilcode/securitywrapper.cpp
@@ -0,0 +1,749 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// File: SecurityWrapper.cpp
+//
+
+//
+// Wrapper around Win32 Security functions
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "securitywrapper.h"
+#include "ex.h"
+#include "holder.h"
+
+
+// For GetSidFromProcess*
+#include <tlhelp32.h>
+#include "wtsapi32.h"
+
+
+//-----------------------------------------------------------------------------
+// Constructor for Sid wrapper class.
+// pSid - OS sid to wrap
+//-----------------------------------------------------------------------------
+Sid::Sid(PSID pSid)
+{
+ _ASSERTE(pSid != NULL);
+ m_pSid = pSid;
+}
+
+//-----------------------------------------------------------------------------
+// Aesthetic wrapper for Sid equality
+//-----------------------------------------------------------------------------
+bool Sid::Equals(PSID a, PSID b)
+{
+ return EqualSid(a, b) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// Ctor for SidBuffer class
+//-----------------------------------------------------------------------------
+SidBuffer::SidBuffer()
+{
+ m_pBuffer = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Dtor for SidBuffer class.
+//-----------------------------------------------------------------------------
+SidBuffer::~SidBuffer()
+{
+ delete [] m_pBuffer;
+}
+
+//-----------------------------------------------------------------------------
+// Get the underlying sid
+// Caller assumes SidBuffer has been initialized.
+//-----------------------------------------------------------------------------
+Sid SidBuffer::GetSid()
+{
+ _ASSERTE(m_pBuffer != NULL);
+ Sid s((PSID) m_pBuffer);
+ return s;
+}
+
+// ----------------------------------------------------------------------------
+// Used by GetSidFromProcessWorker to determine which SID from the
+// process token to use when initializing the SID
+enum SidType
+{
+ // Use TokenOwner: the default owner SID used for newly created objects
+ kOwnerSid,
+
+ // Use TokenUser: the user account from the token
+ kUserSid,
+};
+
+// ----------------------------------------------------------------------------
+// GetSidFromProcessWorker
+//
+// Description:
+// Internal helper. Gets the SID for the given process and given sid type
+//
+// Arguments:
+// * dwProcessId - [in] Process to get SID from
+// * sidType - [in] Type of sid to get (owner or user)
+// * ppSid - [out] SID found. Caller responsible for deleting this memory.
+//
+// Return Value:
+// HRESULT indicating success / failure.
+//
+// Notes:
+// * Caller owns deleting (*ppSid) when done with the SID
+//
+
+HRESULT GetSidFromProcessWorker(DWORD dwProcessId, SidType sidType, PSID *ppSid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ TOKEN_USER *pTokUser = NULL;
+ HANDLE hProc = INVALID_HANDLE_VALUE;
+ HANDLE hToken = INVALID_HANDLE_VALUE;
+ DWORD dwRetLength = 0;
+ LPVOID pvTokenInfo = NULL;
+ TOKEN_INFORMATION_CLASS tokenInfoClass;
+ PSID pSidFromTokenInfo = NULL;
+ DWORD cbSid;
+ PSID pSid = NULL;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcess: 0x%08x\n",
+ dwProcessId));
+
+ _ASSERTE(ppSid);
+ *ppSid = NULL;
+
+ _ASSERTE((sidType == kOwnerSid) || (sidType == kUserSid));
+ tokenInfoClass = (sidType == kOwnerSid) ? TokenOwner : TokenUser;
+
+ hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
+
+ if (hProc == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ if (!OpenProcessToken(hProc, TOKEN_QUERY, &hToken))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // figure out the length
+ GetTokenInformation(hToken, tokenInfoClass, NULL, 0, &dwRetLength);
+ _ASSERTE(dwRetLength);
+
+ pvTokenInfo = new (nothrow) BYTE[dwRetLength];
+ if (pvTokenInfo == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+
+ if (!GetTokenInformation(hToken, tokenInfoClass, pvTokenInfo, dwRetLength, &dwRetLength))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // Copy over the SID
+ pSidFromTokenInfo =
+ (sidType == kOwnerSid) ?
+ ((TOKEN_OWNER *) pvTokenInfo)->Owner :
+ ((TOKEN_USER *) pvTokenInfo)->User.Sid;
+ cbSid = GetLengthSid(pSidFromTokenInfo);
+ pSid = new (nothrow) BYTE[cbSid];
+ if (pSid == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ if (!CopySid(cbSid, pSid, pSidFromTokenInfo))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ }
+
+ *ppSid = pSid;
+ pSid = NULL;
+
+exit:
+ if (hToken != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(hToken);
+ }
+ if (hProc != INVALID_HANDLE_VALUE)
+ {
+ // clean up
+ CloseHandle(hProc);
+ }
+ if (pvTokenInfo)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pvTokenInfo));
+ }
+
+ if (pSid)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pSid));
+ }
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcess return hr : 0x%08x\n",
+ hr));
+
+ return hr;
+}
+
+#ifndef FEATURE_CORESYSTEM
+//-----------------------------------------------------------------------------
+// get the sid of a given process id using WTSEnumerateProcesses
+// @todo: Make this function fail when WTSEnumerateProcesses is not available
+// Or is it always available on all of our platform?
+//
+// Caller remember to call delete on *ppSid
+//-----------------------------------------------------------------------------
+HRESULT GetSidFromProcessEXWorker(DWORD dwProcessId, PSID *ppSid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(ppSid));
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ PWTS_PROCESS_INFOW rgProcessInfo = NULL;
+ DWORD dwNumProcesses;
+ DWORD iProc;
+ DWORD cbSid;
+ PSID pSid = NULL;
+
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx: 0x%08x\n",
+ dwProcessId));
+
+
+ *ppSid = NULL;
+ if (!WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, // use local server
+ 0, // Reserved must be zero
+ 1, // version must be 1
+ &rgProcessInfo, // Receives pointer to process list
+ &dwNumProcesses))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ for (iProc = 0; iProc < dwNumProcesses; iProc++)
+ {
+
+ if (rgProcessInfo[iProc].ProcessId == dwProcessId)
+ {
+ if (rgProcessInfo[iProc].pUserSid == NULL)
+ {
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx is not able to retrieve SID\n"));
+
+ // if there is no Sid for the user, don't call GetLengthSid.
+ // It will crash! It is ok to return E_FAIL as caller will ignore it.
+ hr = E_FAIL;
+ goto exit;
+ }
+ cbSid = GetLengthSid(rgProcessInfo[iProc].pUserSid);
+ pSid = new (nothrow) BYTE[cbSid];
+ if (pSid == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ }
+ else
+ {
+ if (!CopySid(cbSid, pSid, rgProcessInfo[iProc].pUserSid))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ }
+ else
+ {
+ // We are done. Go to exit
+ hr = S_OK;
+ }
+ }
+
+ // we already find a match. Even if we fail from memory allocation of CopySid, still
+ // goto exit.
+ goto exit;
+ }
+ }
+
+ // Walk the whole list and cannot find the matching PID
+ // Find a better error code.
+ hr = E_FAIL;
+
+exit:
+
+ if (rgProcessInfo)
+ {
+ WTSFreeMemory(rgProcessInfo);
+ }
+
+ if (FAILED(hr) && pSid)
+ {
+ delete [] (reinterpret_cast<BYTE*>(pSid));
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ _ASSERTE(pSid);
+ *ppSid = pSid;
+ }
+ LOG((LF_CORDB, LL_INFO10000,
+ "SecurityUtil::GetSidFromProcessEx return hr : 0x%08x\n",
+ hr));
+
+
+ return hr;
+}
+#endif // !FEATURE_CORESYSTEM
+
+//-----------------------------------------------------------------------------
+// The functions below initialize this SidBuffer instance with a Sid from
+// the token of the specified process. The first pair use the OWNER sid from
+// the process token if possible; else use the term serv API to find the
+// USER sid from the process token. This seems a little inconsistent, but
+// remains this way for backward compatibility. The second pair consistently
+// use the USER sid (never the OWNER).
+//
+// While the USER and OWNER sid are often the same, they are not always the
+// same. For example, running a process on win2k3 server as a member of the
+// local admin group causes the USER sid to be the logged-on user, and the
+// OWNER sid to be the local admins group. At least, that's how it was on
+// Monday. Expect this to change randomly at unexpected times, as most
+// security-related behavior does.
+//-----------------------------------------------------------------------------
+
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with a Sid from the token of the specified
+// process. Use the OWNER sid from the process token if possible; else use the term
+// serv API to find the USER sid from the process token. This seems a little
+// inconsistent, but remains this way for backward compatibility.
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+//
+
+HRESULT SidBuffer::InitFromProcessNoThrow(DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_pBuffer == NULL);
+ HRESULT hr = GetSidFromProcessWorker(pid, kOwnerSid, (PSID *) &m_pBuffer);
+#ifndef FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
+ }
+#endif // !FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ _ASSERTE(m_pBuffer != NULL);
+ return S_OK;
+}
+
+// See code:SidBuffer::InitFromProcessNoThrow. Throws if there's an error.
+void SidBuffer::InitFromProcess(DWORD pid)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = InitFromProcessNoThrow(pid);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessAppContainerSidNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with the TokenAppContainerSid from
+// the process token
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+// S_FALSE indicates the process isn't in an AppContainer
+//
+HRESULT SidBuffer::InitFromProcessAppContainerSidNoThrow(DWORD pid)
+{
+ HRESULT hr = S_OK;
+ HANDLE hToken = NULL;
+ BOOL fIsLowBox = FALSE;
+
+ HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+ if (hProcess == NULL)
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+ if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ // Define new TOKEN_INFORMATION_CLASS/ TOKEN_APPCONTAINER_INFORMATION members for Win8 since they are not in the DevDiv copy of WinSDK yet
+ typedef enum _TOKEN_INFORMATION_CLASS_WIN8 {
+ TokenIsAppContainer = TokenLogonSid + 1,
+ TokenCapabilities,
+ TokenAppContainerSid
+ } TOKEN_INFORMATION_CLASS_WIN8;
+
+ typedef struct _TOKEN_APPCONTAINER_INFORMATION
+ {
+ PSID TokenPackage;
+ } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;
+
+ DWORD size;
+ if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIsAppContainer, &fIsLowBox, sizeof(fIsLowBox), &size))
+ {
+ DWORD gle = GetLastError();
+ if (gle == ERROR_INVALID_PARAMETER || gle == ERROR_INVALID_FUNCTION)
+ {
+ hr = S_FALSE; // We are on an OS which doesn't understand LowBox
+ }
+ else
+ {
+ hr = HRESULT_FROM_WIN32(gle);
+ }
+ goto exit;
+ }
+
+ if (!fIsLowBox)
+ {
+ hr = S_FALSE;
+ goto exit;
+ }
+
+ UCHAR PackSid[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_APPCONTAINER_INFORMATION)];
+ if (!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenAppContainerSid, &PackSid, sizeof(PackSid), &size))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ goto exit;
+ }
+
+ {
+ PTOKEN_APPCONTAINER_INFORMATION pTokPack = (PTOKEN_APPCONTAINER_INFORMATION)&PackSid;
+ PSID pLowBoxPackage = pTokPack->TokenPackage;
+ DWORD dwSidLen = GetLengthSid(pLowBoxPackage);
+ m_pBuffer = new (nothrow) BYTE[dwSidLen];
+ if (m_pBuffer == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto exit;
+ }
+ else
+ {
+ if (!CopySid(dwSidLen, m_pBuffer, pLowBoxPackage))
+ {
+ hr = HRESULT_FROM_GetLastError();
+ delete m_pBuffer;
+ m_pBuffer = NULL;
+ goto exit;
+ }
+ }
+ }
+
+exit:
+ if (hProcess != NULL)
+ {
+ CloseHandle(hProcess);
+ }
+ if (hToken != NULL)
+ {
+ CloseHandle(hToken);
+ }
+
+ return hr;
+}
+
+// ----------------------------------------------------------------------------
+// SidBuffer::InitFromProcessUserNoThrow
+//
+// Description:
+// Initialize this SidBuffer instance with a Sid from the token of the specified
+// process. Use the USER sid from the process token if possible; else use the term
+// serv API to find the USER sid from the process token.
+//
+// Arguments:
+// * pid - Process ID from which to grab the SID
+//
+// Return Value:
+// HRESULT indicating success / failure
+//
+
+HRESULT SidBuffer::InitFromProcessUserNoThrow(DWORD pid)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_pBuffer == NULL);
+ HRESULT hr = GetSidFromProcessWorker(pid, kUserSid, (PSID *) &m_pBuffer);
+#ifndef FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ hr = GetSidFromProcessEXWorker(pid, (PSID *) &m_pBuffer);
+ }
+#endif // !FEATURE_CORESYSTEM
+ if (FAILED(hr))
+ {
+ return hr;
+ }
+
+ _ASSERTE(m_pBuffer != NULL);
+ return S_OK;
+}
+
+// See code:SidBuffer::InitFromProcessUserNoThrow. Throws if there's an error.
+void SidBuffer::InitFromProcessUser(DWORD pid)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = InitFromProcessUserNoThrow(pid);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Ctor for Dacl class. Wraps a win32 dacl.
+//-----------------------------------------------------------------------------
+Dacl::Dacl(PACL pAcl)
+{
+ m_acl = pAcl;
+}
+
+//-----------------------------------------------------------------------------
+// Get number of ACE (Access Control Entries) in this DACL.
+//-----------------------------------------------------------------------------
+SIZE_T Dacl::GetAceCount()
+{
+ return (SIZE_T) m_acl->AceCount;
+}
+
+//-----------------------------------------------------------------------------
+// Get Raw a ACE at the given index.
+// Caller assumes index is valid (0 <= dwAceIndex < GetAceCount())
+// Throws on error (which should only be if the index is out of bounds).
+//-----------------------------------------------------------------------------
+ACE_HEADER * Dacl::GetAce(SIZE_T dwAceIndex)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ ACE_HEADER * pAce = NULL;
+ BOOL fOk = ::GetAce(m_acl, (DWORD) dwAceIndex, (LPVOID*) &pAce);
+ _ASSERTE(fOk == (pAce != NULL));
+ if (!fOk)
+ {
+ ThrowLastError();
+ }
+ return pAce;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Ctor for SecurityDescriptor
+//-----------------------------------------------------------------------------
+Win32SecurityDescriptor::Win32SecurityDescriptor()
+{
+ m_pDesc = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Dtor for security Descriptor.
+//-----------------------------------------------------------------------------
+Win32SecurityDescriptor::~Win32SecurityDescriptor()
+{
+ delete [] ((BYTE*) m_pDesc);
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Get the dacl for this security descriptor.
+//-----------------------------------------------------------------------------
+Dacl Win32SecurityDescriptor::GetDacl()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc != NULL);
+
+ BOOL bPresent;
+ BOOL bDaclDefaulted;
+ PACL acl;
+
+ if (GetSecurityDescriptorDacl(m_pDesc, &bPresent, &acl, &bDaclDefaulted) == 0)
+ {
+ ThrowLastError();
+ }
+ if (!bPresent)
+ {
+ // No dacl. We consider this an error because all of the objects we expect
+ // to see should be dacled. If it's not dacled, then it's a malicious user spoofing it.
+ ThrowHR(E_INVALIDARG);
+ }
+
+ Dacl d(acl);
+ return d;
+}
+
+//-----------------------------------------------------------------------------
+// Get the owner from the security descriptor.
+//-----------------------------------------------------------------------------
+HRESULT Win32SecurityDescriptor::GetOwnerNoThrow( PSID* ppSid)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc != NULL);
+ BOOL bOwnerDefaulted;
+
+ if( ppSid == NULL )
+ {
+ return E_INVALIDARG;
+ }
+
+ if (GetSecurityDescriptorOwner(m_pDesc, ppSid, &bOwnerDefaulted) == 0)
+ {
+ DWORD err = GetLastError();
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ return S_OK;
+}
+Sid Win32SecurityDescriptor::GetOwner()
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ PSID pSid;
+ HRESULT hr = GetOwnerNoThrow( &pSid );
+ if( FAILED(hr) )
+ {
+ ThrowHR( hr );
+ }
+
+ Sid s(pSid);
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize this instance of a SecurityDescriptor with the SD for the handle.
+// The handle must have READ_CONTROL permissions to do this.
+// Throws on error.
+//-----------------------------------------------------------------------------
+HRESULT Win32SecurityDescriptor::InitFromHandleNoThrow(HANDLE h)
+{
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ _ASSERTE(m_pDesc == NULL); // only init once.
+
+ DWORD cbNeeded = 0;
+
+ DWORD flags = OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
+
+ // Now get the creator's SID. First get the size of the array needed.
+ BOOL fOk = GetKernelObjectSecurity(h, flags, NULL, 0, &cbNeeded);
+ DWORD err = GetLastError();
+
+ // Caller should give us a handle for which this succeeds. First call will
+ // fail w/ InsufficientBuffer.
+ CONSISTENCY_CHECK_MSGF(fOk || (err == ERROR_INSUFFICIENT_BUFFER), ("Failed to get KernelSecurity for object handle=%p.Err=%d\n", h, err));
+
+ PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR) new(nothrow) BYTE[cbNeeded];
+ if( pSD == NULL )
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (GetKernelObjectSecurity(h, flags, pSD, cbNeeded, &cbNeeded) == 0)
+ {
+ // get last error and fail out.
+ err = GetLastError();
+ delete [] ((BYTE*) pSD);
+ return HRESULT_FROM_WIN32(err);
+ }
+
+ m_pDesc = pSD;
+ return S_OK;
+}
+void Win32SecurityDescriptor::InitFromHandle(HANDLE h)
+{
+ CONTRACTL {
+ THROWS;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = InitFromHandleNoThrow(h);
+ if (FAILED(hr))
+ {
+ ThrowHR(hr);
+ }
+}
diff --git a/src/coreclr/utilcode/sha1.cpp b/src/coreclr/utilcode/sha1.cpp
new file mode 100644
index 00000000000..9c2e091b527
--- /dev/null
+++ b/src/coreclr/utilcode/sha1.cpp
@@ -0,0 +1,368 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+//
+//
+// ===========================================================================
+// File: sha1.cpp
+//
+// ===========================================================================
+/*++
+
+Abstract:
+
+ SHA-1 implementation
+
+Revision History:
+
+--*/
+
+/*
+ File sha1.cpp <STRIP>Version 03 August 2000.</STRIP>
+
+
+ This implements the SHA-1 hash function.
+ For algorithmic background see (for example)
+
+
+ Alfred J. Menezes et al
+ Handbook of Applied Cryptography
+ The CRC Press Series on Discrete Mathematics
+ and its Applications
+ CRC Press LLC, 1997
+ ISBN 0-8495-8523-7
+ QA76.9A25M643
+
+ Also see FIPS 180-1 - Secure Hash Standard,
+ 1993 May 11 and 1995 April 17, by the U.S.
+ National Institute of Standards and Technology (NIST).
+
+*/
+
+#include "stdafx.h"
+#include <utilcode.h> // Utility helpers.
+#include "sha1.h"
+
+typedef const DWORD DWORDC;
+#define ROTATE32L(x,n) _rotl(x,n)
+#define SHAVE32(x) (DWORD)(x)
+
+static void SHA1_block(SHA1_CTX *ctx)
+/*
+ Update the SHA-1 hash from a fresh 64 bytes of data.
+*/
+{
+ static DWORDC sha1_round1 = 0x5A827999u;
+ static DWORDC sha1_round2 = 0x6ED9EBA1u;
+ static DWORDC sha1_round3 = 0x8F1BBCDCu;
+ static DWORDC sha1_round4 = 0xCA62C1D6u;
+
+ DWORD a = ctx->partial_hash[0], b = ctx->partial_hash[1];
+ DWORD c = ctx->partial_hash[2], d = ctx->partial_hash[3];
+ DWORD e = ctx->partial_hash[4];
+ DWORD msg80[80];
+ int i;
+ BOOL OK = TRUE;
+
+ // OACR note:
+ // Loop conditions are using (i <= limit - increment) instead of (i < limit) to satisfy OACR. When the increment is greater
+ // than 1, OACR incorrectly thinks that the max value of 'i' is (limit - 1).
+
+ for (i = 0; i < 16; i++) { // Copy to local array, zero original
+ // Extend length to 80
+ DWORDC datval = ctx->awaiting_data[i];
+ ctx->awaiting_data[i] = 0;
+ msg80[i] = datval;
+ }
+
+ for (i = 16; i <= 80 - 2; i += 2) {
+ DWORDC temp1 = msg80[i-3] ^ msg80[i-8]
+ ^ msg80[i-14] ^ msg80[i-16];
+ DWORDC temp2 = msg80[i-2] ^ msg80[i-7]
+ ^ msg80[i-13] ^ msg80[i-15];
+ msg80[i ] = ROTATE32L(temp1, 1);
+ msg80[i+1] = ROTATE32L(temp2, 1);
+ }
+
+#define ROUND1(B, C, D) (((D) ^ ((B) & ((C) ^ (D)))) + sha1_round1)
+ // Equivalent to (B & C) | (~B & D).
+ // (check cases B = 0 and B = 1)
+#define ROUND2(B, C, D) (((B) ^ (C) ^ (D)) + sha1_round2)
+
+#define ROUND3(B, C, D) ((((C) & ((B) | (D))) | ((B) & (D))) + sha1_round3)
+
+#define ROUND4(B, C, D) (((B) ^ (C) ^ (D)) + sha1_round4)
+
+// Round 1
+ for (i = 0; i <= 20 - 5; i += 5) {
+ e += ROTATE32L(a, 5) + ROUND1(b, c, d) + msg80[i];
+ b = ROTATE32L(b, 30);
+
+ d += ROTATE32L(e, 5) + ROUND1(a, b, c) + msg80[i+1];
+ a = ROTATE32L(a, 30);
+
+ c += ROTATE32L(d, 5) + ROUND1(e, a, b) + msg80[i+2];
+ e = ROTATE32L(e, 30);
+
+ b += ROTATE32L(c, 5) + ROUND1(d, e, a) + msg80[i+3];
+ d = ROTATE32L(d, 30);
+
+ a += ROTATE32L(b, 5) + ROUND1(c, d, e) + msg80[i+4];
+ c = ROTATE32L(c, 30);
+#if 0
+ printf("i = %ld %08lx %08lx %08lx %08lx %08lx\n",
+ i, a, b, c, d, e);
+#endif
+ } // for i
+
+// Round 2
+ for (i = 20; i <= 40 - 5; i += 5) {
+ e += ROTATE32L(a, 5) + ROUND2(b, c, d) + msg80[i];
+ b = ROTATE32L(b, 30);
+
+ d += ROTATE32L(e, 5) + ROUND2(a, b, c) + msg80[i+1];
+ a = ROTATE32L(a, 30);
+
+ c += ROTATE32L(d, 5) + ROUND2(e, a, b) + msg80[i+2];
+ e = ROTATE32L(e, 30);
+
+ b += ROTATE32L(c, 5) + ROUND2(d, e, a) + msg80[i+3];
+ d = ROTATE32L(d, 30);
+
+ a += ROTATE32L(b, 5) + ROUND2(c, d, e) + msg80[i+4];
+ c = ROTATE32L(c, 30);
+ } // for i
+
+// Round 3
+ for (i = 40; i <= 60 - 5; i += 5) {
+ e += ROTATE32L(a, 5) + ROUND3(b, c, d) + msg80[i];
+ b = ROTATE32L(b, 30);
+
+ d += ROTATE32L(e, 5) + ROUND3(a, b, c) + msg80[i+1];
+ a = ROTATE32L(a, 30);
+
+ c += ROTATE32L(d, 5) + ROUND3(e, a, b) + msg80[i+2];
+ e = ROTATE32L(e, 30);
+
+ b += ROTATE32L(c, 5) + ROUND3(d, e, a) + msg80[i+3];
+ d = ROTATE32L(d, 30);
+
+ a += ROTATE32L(b, 5) + ROUND3(c, d, e) + msg80[i+4];
+ c = ROTATE32L(c, 30);
+ } // for i
+
+// Round 4
+ for (i = 60; i <= 80 - 5; i += 5) {
+ e += ROTATE32L(a, 5) + ROUND4(b, c, d) + msg80[i];
+ b = ROTATE32L(b, 30);
+
+ d += ROTATE32L(e, 5) + ROUND4(a, b, c) + msg80[i+1];
+ a = ROTATE32L(a, 30);
+
+ c += ROTATE32L(d, 5) + ROUND4(e, a, b) + msg80[i+2];
+ e = ROTATE32L(e, 30);
+
+ b += ROTATE32L(c, 5) + ROUND4(d, e, a) + msg80[i+3];
+ d = ROTATE32L(d, 30);
+
+ a += ROTATE32L(b, 5) + ROUND4(c, d, e) + msg80[i+4];
+ c = ROTATE32L(c, 30);
+ } // for i
+
+#undef ROUND1
+#undef ROUND2
+#undef ROUND3
+#undef ROUND4
+
+ ctx->partial_hash[0] += a;
+ ctx->partial_hash[1] += b;
+ ctx->partial_hash[2] += c;
+ ctx->partial_hash[3] += d;
+ ctx->partial_hash[4] += e;
+#if 0
+ for (i = 0; i < 16; i++) {
+ printf("%8lx ", msg16[i]);
+ if ((i & 7) == 7) printf("\n");
+ }
+ printf("a, b, c, d, e = %08lx %08lx %08lx %08lx %08lx\n",
+ a, b, c, d, e);
+ printf("Partial hash = %08lx %08lx %08lx %08lx %08lx\n",
+ (long)ctx->partial_hash[0], (long)ctx->partial_hash[1],
+ (long)ctx->partial_hash[2], (long)ctx->partial_hash[3],
+ (long)ctx->partial_hash[4]);
+#endif
+} // end SHA1_block
+
+
+void SHA1Hash::SHA1Init(SHA1_CTX *ctx)
+{
+ ctx->nbit_total[0] = ctx->nbit_total[1] = 0;
+
+ for (DWORD i = 0; i != 16; i++) {
+ ctx->awaiting_data[i] = 0;
+ }
+
+ /*
+ Initialize hash variables.
+
+ */
+
+ ctx->partial_hash[0] = 0x67452301u;
+ ctx->partial_hash[1] = 0xefcdab89u;
+ ctx->partial_hash[2] = ~ctx->partial_hash[0];
+ ctx->partial_hash[3] = ~ctx->partial_hash[1];
+ ctx->partial_hash[4] = 0xc3d2e1f0u;
+
+}
+
+void SHA1Hash::SHA1Update(
+ SHA1_CTX * ctx, // IN/OUT
+ const BYTE * msg, // IN
+ DWORD nbyte) // IN
+/*
+ Append data to a partially hashed SHA-1 message.
+*/
+{
+ const BYTE *fresh_data = msg;
+ DWORD nbyte_left = nbyte;
+ DWORD nbit_occupied = ctx->nbit_total[0] & 511;
+ DWORD *awaiting_data;
+ DWORDC nbitnew_low = SHAVE32(8*nbyte);
+
+
+ _ASSERTE((nbit_occupied & 7) == 0); // Partial bytes not implemented
+
+ ctx->nbit_total[0] += nbitnew_low;
+ ctx->nbit_total[1] += (nbyte >> 29)
+ + (SHAVE32(ctx->nbit_total[0]) < nbitnew_low);
+
+ /* Advance to word boundary in waiting_data */
+
+ if ((nbit_occupied & 31) != 0) {
+ awaiting_data = ctx->awaiting_data + nbit_occupied/32;
+
+ while ((nbit_occupied & 31) != 0 && nbyte_left != 0) {
+ nbit_occupied += 8;
+ *awaiting_data |= (DWORD)*fresh_data++
+ << ((-(int)nbit_occupied) & 31);
+ nbyte_left--; // Start at most significant byte
+ }
+ } // if nbit_occupied
+
+ /* Transfer 4 bytes at a time */
+
+ do {
+ DWORDC nword_occupied = nbit_occupied/32;
+ DWORD nwcopy = min(nbyte_left/4, 16 - nword_occupied);
+ _ASSERTE (nbit_occupied <= 512);
+ _ASSERTE ((nbit_occupied & 31) == 0 || nbyte_left == 0);
+ awaiting_data = ctx->awaiting_data + nword_occupied;
+ nbyte_left -= 4*nwcopy;
+ nbit_occupied += 32*nwcopy;
+
+ while (nwcopy != 0) {
+ DWORDC byte0 = (DWORD)fresh_data[0];
+ DWORDC byte1 = (DWORD)fresh_data[1];
+ DWORDC byte2 = (DWORD)fresh_data[2];
+ DWORDC byte3 = (DWORD)fresh_data[3];
+ *awaiting_data++ = byte3 | (byte2 << 8)
+ | (byte1 << 16) | (byte0 << 24);
+ /* Big endian */
+ fresh_data += 4;
+ nwcopy--;
+ }
+
+ if (nbit_occupied == 512) {
+ SHA1_block(ctx);
+ nbit_occupied = 0;
+ awaiting_data -= 16;
+ _ASSERTE(awaiting_data == ctx->awaiting_data);
+ }
+ } while (nbyte_left >= 4);
+
+ _ASSERTE (ctx->awaiting_data + nbit_occupied/32
+ == awaiting_data);
+
+ while (nbyte_left != 0) {
+ DWORDC new_byte = (DWORD)*fresh_data++;
+
+ _ASSERTE((nbit_occupied & 31) <= 16);
+ nbit_occupied += 8;
+ *awaiting_data |= new_byte << ((-(int)nbit_occupied) & 31);
+ nbyte_left--;
+ }
+
+ _ASSERTE (nbit_occupied == (ctx->nbit_total[0] & 511));
+} // end SHA1Update
+
+
+
+void SHA1Hash::SHA1Final(
+ SHA1_CTX * ctx, // IN/OUT
+ BYTE * digest) // OUT
+/*
+ Finish a SHA-1 hash.
+*/
+{
+ DWORDC nbit0 = ctx->nbit_total[0];
+ DWORDC nbit1 = ctx->nbit_total[1];
+ DWORD nbit_occupied = nbit0 & 511;
+ DWORD i;
+
+ _ASSERTE((nbit_occupied & 7) == 0);
+
+ ctx->awaiting_data[nbit_occupied/32]
+ |= (DWORD)0x80 << ((-8-nbit_occupied) & 31);
+ // Append a 1 bit
+ nbit_occupied += 8;
+
+
+ // Append zero bits until length (in bits) is 448 mod 512.
+ // Then append the length, in bits.
+ // Here we assume the buffer was zeroed earlier.
+
+ if (nbit_occupied > 448) { // If fewer than 64 bits left
+ SHA1_block(ctx);
+ nbit_occupied = 0;
+ }
+ ctx->awaiting_data[14] = nbit1;
+ ctx->awaiting_data[15] = nbit0;
+ SHA1_block(ctx);
+
+ /* Copy final digest to user-supplied byte array */
+
+ for (i = 0; i != 5; i++) {
+ DWORDC dwi = ctx->partial_hash[i];
+ digest[4*i + 0] = (BYTE)((dwi >> 24) & 255);
+ digest[4*i + 1] = (BYTE)((dwi >> 16) & 255);
+ digest[4*i + 2] = (BYTE)((dwi >> 8) & 255);
+ digest[4*i + 3] = (BYTE)(dwi & 255); // Big-endian
+ }
+} // end SHA1Final
+
+SHA1Hash::SHA1Hash()
+{
+ m_fFinalized = FALSE;
+ SHA1Init(&m_Context);
+}
+
+void SHA1Hash::AddData(BYTE *pbData, DWORD cbData)
+{
+ if (m_fFinalized)
+ return;
+
+ SHA1Update(&m_Context, pbData, cbData);
+}
+
+// Retrieve a pointer to the final hash.
+BYTE *SHA1Hash::GetHash()
+{
+ if (m_fFinalized)
+ return m_Value;
+
+ SHA1Final(&m_Context, m_Value);
+
+ m_fFinalized = TRUE;
+
+ return m_Value;
+}
diff --git a/src/coreclr/utilcode/sigbuilder.cpp b/src/coreclr/utilcode/sigbuilder.cpp
new file mode 100644
index 00000000000..368815c55b5
--- /dev/null
+++ b/src/coreclr/utilcode/sigbuilder.cpp
@@ -0,0 +1,164 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+#include "stdafx.h"
+#include "sigbuilder.h"
+#include "ex.h"
+
+void SigBuilder::AppendByte(BYTE b)
+{
+ STANDARD_VM_CONTRACT;
+
+ Ensure(1);
+ m_pBuffer[m_dwLength++] = b;
+}
+
+void SigBuilder::AppendData(ULONG data)
+{
+ STANDARD_VM_CONTRACT;
+
+ //
+ // Inlined logic from CorSigCompressData
+ //
+
+ if (data <= 0x7F)
+ {
+ Ensure(1);
+ m_pBuffer[m_dwLength++] = BYTE(data);
+ return;
+ }
+
+ if (data <= 0x3FFF)
+ {
+ Ensure(2);
+
+ DWORD dwLength = m_dwLength;
+ BYTE * pBuffer = m_pBuffer;
+
+ pBuffer[dwLength] = BYTE((data >> 8) | 0x80);
+ pBuffer[dwLength+1] = BYTE(data);
+
+ m_dwLength = dwLength + 2;
+ return;
+ }
+
+ if (data <= 0x1FFFFFFF)
+ {
+ Ensure(4);
+
+ DWORD dwLength = m_dwLength;
+ BYTE * pBuffer = m_pBuffer;
+
+ pBuffer[dwLength] = BYTE((data >> 24) | 0xC0);
+ pBuffer[dwLength+1] = BYTE(data >> 16);
+ pBuffer[dwLength+2] = BYTE(data >> 8);
+ pBuffer[dwLength+3] = BYTE(data);
+
+ m_dwLength = dwLength + 4;
+ return;
+ }
+
+ // We currently can only represent to 0x1FFFFFFF.
+ ThrowHR(COR_E_OVERFLOW);
+}
+
+void SigBuilder::AppendToken(mdToken tk)
+{
+ STANDARD_VM_CONTRACT;
+
+ //
+ // Inlined logic from CorSigCompressToken
+ //
+
+ RID rid = RidFromToken(tk);
+ ULONG32 ulTyp = TypeFromToken(tk);
+
+ _ASSERTE(rid <= 0x3FFFFFF);
+ rid = (rid << 2);
+
+ // TypeDef is encoded with low bits 00
+ // TypeRef is encoded with low bits 01
+ // TypeSpec is encoded with low bits 10
+ // BaseType is encoded with low bit 11
+ //
+ if (ulTyp == CorSigDecodeTokenType(0))
+ {
+ // make the last two bits 00
+ // nothing to do
+ }
+ else if (ulTyp == CorSigDecodeTokenType(1))
+ {
+ // make the last two bits 01
+ rid |= 0x1;
+ }
+ else if (ulTyp == CorSigDecodeTokenType(2))
+ {
+ // make last two bits 0
+ rid |= 0x2;
+ }
+ else if (ulTyp == CorSigDecodeTokenType(3))
+ {
+ rid |= 0x3;
+ }
+ else
+ {
+ ThrowHR(COR_E_BADIMAGEFORMAT);
+ }
+
+ AppendData(rid);
+}
+
+void SigBuilder::AppendBlob(const PVOID pBlob, SIZE_T cbBlob)
+{
+ STANDARD_VM_CONTRACT;
+
+ Ensure(cbBlob);
+ memcpy(m_pBuffer + m_dwLength, pBlob, cbBlob);
+ m_dwLength += (DWORD)cbBlob;
+}
+
+void SigBuilder::Grow(SIZE_T cbMin)
+{
+ STANDARD_VM_CONTRACT;
+
+ DWORD dwNewAllocation = max(m_dwLength + (DWORD)cbMin, 2 * m_dwAllocation);
+
+ // Overflow checks
+ if (dwNewAllocation < m_dwLength || (dwNewAllocation - m_dwLength) < cbMin)
+ ThrowOutOfMemory();
+
+ BYTE * pNewAllocation = new BYTE[dwNewAllocation];
+ memcpy(pNewAllocation, m_pBuffer, m_dwLength);
+
+ BYTE * pOldAllocation = m_pBuffer;
+
+ m_pBuffer = pNewAllocation;
+ m_dwAllocation = dwNewAllocation;
+
+ if (pOldAllocation != m_prealloc)
+ delete [] pOldAllocation;
+}
+
+SigBuilder::~SigBuilder()
+{
+ if (m_pBuffer != m_prealloc)
+ delete [] m_pBuffer;
+}
+
+SigBuilder::SigBuilder(DWORD cbPreallocationSize)
+{
+ STANDARD_VM_CONTRACT;
+
+ m_dwLength = 0;
+ if (cbPreallocationSize <= sizeof(m_prealloc))
+ {
+ m_pBuffer = m_prealloc;
+ m_dwAllocation = sizeof(m_prealloc);
+ }
+ else
+ {
+ m_pBuffer = new BYTE[cbPreallocationSize];
+ m_dwAllocation = cbPreallocationSize;
+ }
+}
diff --git a/src/coreclr/utilcode/sigparser.cpp b/src/coreclr/utilcode/sigparser.cpp
new file mode 100644
index 00000000000..890d508c7aa
--- /dev/null
+++ b/src/coreclr/utilcode/sigparser.cpp
@@ -0,0 +1,194 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//
+// sigparser.cpp
+//
+
+//
+// Signature parsing code
+//
+#include "stdafx.h"
+#include "sigparser.h"
+#include "contract.h"
+
+HRESULT SigParser::SkipExactlyOne()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ CorElementType typ;
+ HRESULT hr = GetElemType(&typ);
+
+ IfFailRet(hr);
+
+ if (!CorIsPrimitiveType(typ))
+ {
+ switch ((DWORD)typ)
+ {
+ default:
+ // _ASSERT(!"Illegal or unimplement type in COM+ sig.");
+ return META_E_BAD_SIGNATURE;
+ break;
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ IfFailRet(GetData(NULL)); // Skip variable number
+ break;
+ case ELEMENT_TYPE_VAR_ZAPSIG:
+ IfFailRet(GetData(NULL)); // Skip RID
+ break;
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_CANON_ZAPSIG:
+ break;
+
+ case ELEMENT_TYPE_BYREF: //fallthru
+ case ELEMENT_TYPE_PTR:
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ case ELEMENT_TYPE_NATIVE_VALUETYPE_ZAPSIG:
+ IfFailRet(SkipExactlyOne()); // Skip referenced type
+ break;
+
+ case ELEMENT_TYPE_VALUETYPE: //fallthru
+ case ELEMENT_TYPE_CLASS:
+ IfFailRet(GetToken(NULL)); // Skip RID
+ break;
+
+ case ELEMENT_TYPE_MODULE_ZAPSIG:
+ IfFailRet(GetData(NULL)); // Skip index
+ IfFailRet(SkipExactlyOne()); // Skip type
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ IfFailRet(SkipSignature());
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ {
+ IfFailRet(SkipExactlyOne()); // Skip element type
+ ULONG rank;
+ IfFailRet(GetData(&rank)); // Get rank
+ if (rank)
+ {
+ ULONG nsizes;
+ IfFailRet(GetData(&nsizes)); // Get # of sizes
+ while (nsizes--)
+ {
+ IfFailRet(GetData(NULL)); // Skip size
+ }
+
+ ULONG nlbounds;
+ IfFailRet(GetData(&nlbounds)); // Get # of lower bounds
+ while (nlbounds--)
+ {
+ IfFailRet(GetData(NULL)); // Skip lower bounds
+ }
+ }
+
+ }
+ break;
+
+ case ELEMENT_TYPE_SENTINEL:
+ // Should be unreachable since GetElem strips it
+ break;
+
+ case ELEMENT_TYPE_INTERNAL:
+ IfFailRet(GetPointer(NULL));
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ IfFailRet(SkipExactlyOne()); // Skip generic type
+ ULONG argCnt;
+ IfFailRet(GetData(&argCnt)); // Get number of parameters
+ while (argCnt--)
+ {
+ IfFailRet(SkipExactlyOne()); // Skip the parameters
+ }
+ break;
+
+ }
+ }
+
+ return hr;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Skip only a method header signature - not the sigs of the args to the method.
+//
+HRESULT
+SigParser::SkipMethodHeaderSignature(
+ ULONG * pcArgs)
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // Skip calling convention
+ ULONG uCallConv;
+ IfFailRet(GetCallingConvInfo(&uCallConv));
+
+ if ((uCallConv == IMAGE_CEE_CS_CALLCONV_FIELD) ||
+ (uCallConv == IMAGE_CEE_CS_CALLCONV_LOCAL_SIG))
+ {
+ return META_E_BAD_SIGNATURE;
+ }
+
+ // Skip type parameter count
+ if (uCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ IfFailRet(GetData(NULL));
+
+ // Get arg count;
+ IfFailRet(GetData(pcArgs));
+
+ // Skip return type;
+ IfFailRet(SkipExactlyOne());
+
+ return hr;
+} // SigParser::SkipMethodHeaderSignature
+
+//---------------------------------------------------------------------------------------
+//
+// Skip a sub signature (as immediately follows an ELEMENT_TYPE_FNPTR).
+HRESULT SigParser::SkipSignature()
+{
+ CONTRACTL
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ ULONG cArgs;
+
+ IfFailRet(SkipMethodHeaderSignature(&cArgs));
+
+ // Skip args.
+ while (cArgs) {
+ IfFailRet(SkipExactlyOne());
+ cArgs--;
+ }
+
+ return hr;
+}
diff --git a/src/coreclr/utilcode/splitpath.cpp b/src/coreclr/utilcode/splitpath.cpp
new file mode 100644
index 00000000000..b087e6cd3d1
--- /dev/null
+++ b/src/coreclr/utilcode/splitpath.cpp
@@ -0,0 +1,258 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+/***
+*splitpath.c - break down path name into components
+*
+
+*
+*Purpose:
+* To provide support for accessing the individual components of an
+* arbitrary path name
+*
+*******************************************************************************/
+#include "stdafx.h"
+#include "winwrap.h"
+#include "utilcode.h"
+#include "sstring.h"
+
+
+/***
+*SplitPath() - split a path name into its individual components
+*
+*Purpose:
+* to split a path name into its individual components
+*
+*Entry:
+* path - pointer to path name to be parsed
+* drive - pointer to buffer for drive component, if any
+* dir - pointer to buffer for subdirectory component, if any
+* fname - pointer to buffer for file base name component, if any
+* ext - pointer to buffer for file name extension component, if any
+*
+*Exit:
+* drive - pointer to drive string. Includes ':' if a drive was given.
+* dir - pointer to subdirectory string. Includes leading and trailing
+* '/' or '\', if any.
+* fname - pointer to file base name
+* ext - pointer to file extension, if any. Includes leading '.'.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void SplitPath(
+ const WCHAR *path,
+ __inout_z __inout_ecount_opt(driveSizeInWords) WCHAR *drive, int driveSizeInWords,
+ __inout_z __inout_ecount_opt(dirSizeInWords) WCHAR *dir, int dirSizeInWords,
+ __inout_z __inout_ecount_opt(fnameSizeInWords) WCHAR *fname, size_t fnameSizeInWords,
+ __inout_z __inout_ecount_opt(extSizeInWords) WCHAR *ext, size_t extSizeInWords)
+{
+ WRAPPER_NO_CONTRACT;
+
+ LPCWSTR _wszDrive, _wszDir, _wszFileName, _wszExt;
+ size_t _cchDrive, _cchDir, _cchFileName, _cchExt;
+
+ SplitPathInterior(path,
+ &_wszDrive, &_cchDrive,
+ &_wszDir, &_cchDir,
+ &_wszFileName, &_cchFileName,
+ &_wszExt, &_cchExt);
+
+ if (drive && _wszDrive)
+ wcsncpy_s(drive, driveSizeInWords, _wszDrive, min(_cchDrive, _MAX_DRIVE));
+
+ if (dir && _wszDir)
+ wcsncpy_s(dir, dirSizeInWords, _wszDir, min(_cchDir, _MAX_DIR));
+
+ if (fname && _wszFileName)
+ wcsncpy_s(fname, fnameSizeInWords, _wszFileName, min(_cchFileName, _MAX_FNAME));
+
+ if (ext && _wszExt)
+ wcsncpy_s(ext, extSizeInWords, _wszExt, min(_cchExt, _MAX_EXT));
+}
+
+//*******************************************************************************
+// A much more sensible version that just points to each section of the string.
+//*******************************************************************************
+void SplitPathInterior(
+ __in LPCWSTR wszPath,
+ __out_opt LPCWSTR *pwszDrive, __out_opt size_t *pcchDrive,
+ __out_opt LPCWSTR *pwszDir, __out_opt size_t *pcchDir,
+ __out_opt LPCWSTR *pwszFileName, __out_opt size_t *pcchFileName,
+ __out_opt LPCWSTR *pwszExt, __out_opt size_t *pcchExt)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Arguments must come in valid pairs
+ _ASSERTE(!!pwszDrive == !!pcchDrive);
+ _ASSERTE(!!pwszDir == !!pcchDir);
+ _ASSERTE(!!pwszFileName == !!pcchFileName);
+ _ASSERTE(!!pwszExt == !!pcchExt);
+
+ WCHAR *p;
+ LPCWSTR last_slash = NULL, dot = NULL;
+
+ /* we assume that the path argument has the following form, where any
+ * or all of the components may be missing.
+ *
+ * <drive><dir><fname><ext>
+ *
+ * and each of the components has the following expected form(s)
+ *
+ * drive:
+ * 0 to _MAX_DRIVE-1 characters, the last of which, if any, is a
+ * ':'
+ * dir:
+ * 0 to _MAX_DIR-1 characters in the form of an absolute path
+ * (leading '/' or '\') or relative path, the last of which, if
+ * any, must be a '/' or '\'. E.g -
+ * absolute path:
+ * \top\next\last\ ; or
+ * /top/next/last/
+ * relative path:
+ * top\next\last\ ; or
+ * top/next/last/
+ * Mixed use of '/' and '\' within a path is also tolerated
+ * fname:
+ * 0 to _MAX_FNAME-1 characters not including the '.' character
+ * ext:
+ * 0 to _MAX_EXT-1 characters where, if any, the first must be a
+ * '.'
+ *
+ */
+
+ /* extract drive letter and :, if any */
+
+ if ((wcslen(wszPath) > (_MAX_DRIVE - 2)) && (*(wszPath + _MAX_DRIVE - 2) == _T(':'))) {
+ if (pwszDrive && pcchDrive) {
+ *pwszDrive = wszPath;
+ *pcchDrive = _MAX_DRIVE - 1;
+ }
+ wszPath += _MAX_DRIVE - 1;
+ }
+ else if (pwszDrive && pcchDrive) {
+ *pwszDrive = NULL;
+ *pcchDrive = 0;
+ }
+
+ /* extract path string, if any. Path now points to the first character
+ * of the path, if any, or the filename or extension, if no path was
+ * specified. Scan ahead for the last occurrence, if any, of a '/' or
+ * '\' path separator character. If none is found, there is no path.
+ * We will also note the last '.' character found, if any, to aid in
+ * handling the extension.
+ */
+
+ for (last_slash = NULL, p = (WCHAR *)wszPath; *p; p++) {
+ if (*p == _T('/') || *p == _T('\\'))
+ /* point to one beyond for later copy */
+ last_slash = p + 1;
+ else if (*p == _T('.'))
+ dot = p;
+ }
+
+ if (last_slash) {
+ /* found a path - copy up through last_slash or max. characters
+ * allowed, whichever is smaller
+ */
+
+ if (pwszDir && pcchDir) {
+ *pwszDir = wszPath;
+ *pcchDir = last_slash - wszPath;
+ }
+ wszPath = last_slash;
+ }
+ else if (pwszDir && pcchDir) {
+ *pwszDir = NULL;
+ *pcchDir = 0;
+ }
+
+ /* extract file name and extension, if any. Path now points to the
+ * first character of the file name, if any, or the extension if no
+ * file name was given. Dot points to the '.' beginning the extension,
+ * if any.
+ */
+
+ if (dot && (dot >= wszPath)) {
+ /* found the marker for an extension - copy the file name up to
+ * the '.'.
+ */
+ if (pwszFileName && pcchFileName) {
+ *pwszFileName = wszPath;
+ *pcchFileName = dot - wszPath;
+ }
+ /* now we can get the extension - remember that p still points
+ * to the terminating nul character of path.
+ */
+ if (pwszExt && pcchExt) {
+ *pwszExt = dot;
+ *pcchExt = p - dot;
+ }
+ }
+ else {
+ /* found no extension, give empty extension and copy rest of
+ * string into fname.
+ */
+ if (pwszFileName && pcchFileName) {
+ *pwszFileName = wszPath;
+ *pcchFileName = p - wszPath;
+ }
+ if (pwszExt && pcchExt) {
+ *pwszExt = NULL;
+ *pcchExt = 0;
+ }
+ }
+}
+
+/***
+*SplitPath() - split a path name into its individual components
+*
+*Purpose:
+* to split a path name into its individual components
+*
+*Entry:
+* path - SString representing the path name to be parsed
+* drive - Out SString for drive component
+* dir - Out SString for subdirectory component
+* fname - Out SString for file base name component
+* ext - Out SString for file name extension component
+*
+*Exit:
+* drive - Drive string. Includes ':' if a drive was given.
+* dir - Subdirectory string. Includes leading and trailing
+* '/' or '\', if any.
+* fname - File base name
+* ext - File extension, if any. Includes leading '.'.
+*
+*Exceptions:
+*
+*******************************************************************************/
+
+void SplitPath(__in SString const &path,
+ __inout_opt SString *drive,
+ __inout_opt SString *dir,
+ __inout_opt SString *fname,
+ __inout_opt SString *ext)
+{
+ LPCWSTR wzDrive, wzDir, wzFname, wzExt;
+ size_t cchDrive, cchDir, cchFname, cchExt;
+
+ SplitPathInterior(path,
+ &wzDrive, &cchDrive,
+ &wzDir, &cchDir,
+ &wzFname, &cchFname,
+ &wzExt, &cchExt);
+
+ if (drive != NULL)
+ drive->Set(wzDrive, (COUNT_T)cchDrive);
+
+ if (dir != NULL)
+ dir->Set(wzDir, (COUNT_T)cchDir);
+
+ if (fname != NULL)
+ fname->Set(wzFname, (COUNT_T)cchFname);
+
+ if (ext != NULL)
+ ext->Set(wzExt, (COUNT_T)cchExt);
+}
+
diff --git a/src/coreclr/utilcode/sstring.cpp b/src/coreclr/utilcode/sstring.cpp
new file mode 100644
index 00000000000..a0017a5a003
--- /dev/null
+++ b/src/coreclr/utilcode/sstring.cpp
@@ -0,0 +1,2790 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// SString.cpp
+//
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sstring.h"
+#include "ex.h"
+#include "holder.h"
+
+
+#if defined(_MSC_VER)
+#pragma inline_depth (25)
+#endif
+
+//-----------------------------------------------------------------------------
+// Static variables
+//-----------------------------------------------------------------------------
+
+// Have one internal, well-known, literal for the empty string.
+const BYTE SString::s_EmptyBuffer[2] = { 0 };
+
+// @todo: these need to be initialized by calling GetACP()
+
+UINT SString::s_ACP = 0;
+
+#ifndef DACCESS_COMPILE
+static BYTE s_EmptySpace[sizeof(SString)] = { 0 };
+#endif // DACCESS_COMPILE
+
+SPTR_IMPL(SString,SString,s_Empty);
+
+void SString::Startup()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (s_ACP == 0)
+ {
+ UINT ACP = GetACP();
+
+#ifndef DACCESS_COMPILE
+ s_Empty = PTR_SString(new (s_EmptySpace) SString());
+ s_Empty->SetNormalized();
+#endif // DACCESS_COMPILE
+
+ MemoryBarrier();
+ s_ACP = ACP;
+ }
+}
+
+CHECK SString::CheckStartup()
+{
+ WRAPPER_NO_CONTRACT;
+
+ CHECK(s_Empty != NULL);
+ CHECK_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Case insensitive helpers.
+//-----------------------------------------------------------------------------
+
+static WCHAR MapChar(WCHAR wc, DWORD dwFlags)
+{
+ WRAPPER_NO_CONTRACT;
+
+ WCHAR wTmp;
+
+#ifndef TARGET_UNIX
+
+ int iRet = ::LCMapStringEx(LOCALE_NAME_INVARIANT, dwFlags, &wc, 1, &wTmp, 1, NULL, NULL, 0);
+ if (!iRet) {
+ // This can fail in non-exceptional cases becauseof unknown unicode characters.
+ wTmp = wc;
+ }
+
+#else // !TARGET_UNIX
+ // For PAL, no locale specific processing is done
+
+ if (dwFlags == LCMAP_UPPERCASE)
+ {
+ wTmp =
+#ifdef SELF_NO_HOST
+ toupper(wc);
+#else
+ PAL_ToUpperInvariant(wc);
+#endif
+ }
+ else
+ {
+ _ASSERTE(dwFlags == LCMAP_LOWERCASE);
+ wTmp =
+#ifdef SELF_NO_HOST
+ tolower(wc);
+#else
+ PAL_ToLowerInvariant(wc);
+#endif
+ }
+#endif // !TARGET_UNIX
+
+ return wTmp;
+}
+
+#define IS_UPPER_A_TO_Z(x) (((x) >= W('A')) && ((x) <= W('Z')))
+#define IS_LOWER_A_TO_Z(x) (((x) >= W('a')) && ((x) <= W('z')))
+#define CAN_SIMPLE_UPCASE(x) (((x)&~0x7f) == 0)
+#define CAN_SIMPLE_DOWNCASE(x) (((x)&~0x7f) == 0)
+#define SIMPLE_UPCASE(x) (IS_LOWER_A_TO_Z(x) ? ((x) - W('a') + W('A')) : (x))
+#define SIMPLE_DOWNCASE(x) (IS_UPPER_A_TO_Z(x) ? ((x) - W('A') + W('a')) : (x))
+
+/* static */
+int SString::CaseCompareHelper(const WCHAR *buffer1, const WCHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(stopOnNull || stopOnCount);
+
+ const WCHAR *buffer1End = buffer1 + count;
+ int diff = 0;
+
+ while (!stopOnCount || (buffer1 < buffer1End))
+ {
+ WCHAR ch1 = *buffer1++;
+ WCHAR ch2 = *buffer2++;
+ diff = ch1 - ch2;
+ if ((ch1 == 0) || (ch2 == 0))
+ {
+ if (diff != 0 || stopOnNull)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (diff != 0)
+ {
+ diff = ((CAN_SIMPLE_UPCASE(ch1) ? SIMPLE_UPCASE(ch1) : MapChar(ch1, LCMAP_UPPERCASE))
+ - (CAN_SIMPLE_UPCASE(ch2) ? SIMPLE_UPCASE(ch2) : MapChar(ch2, LCMAP_UPPERCASE)));
+ }
+ if (diff != 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return diff;
+}
+
+#define IS_LOWER_A_TO_Z_ANSI(x) (((x) >= 'a') && ((x) <= 'z'))
+#define CAN_SIMPLE_UPCASE_ANSI(x) (((x) >= 0x20) && ((x) <= 0x7f))
+#define SIMPLE_UPCASE_ANSI(x) (IS_LOWER_A_TO_Z(x) ? ((x) - 'a' + 'A') : (x))
+
+/* static */
+int SString::CaseCompareHelperA(const CHAR *buffer1, const CHAR *buffer2, COUNT_T count, BOOL stopOnNull, BOOL stopOnCount)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE(stopOnNull || stopOnCount);
+
+ const CHAR *buffer1End = buffer1 + count;
+ int diff = 0;
+
+ while (!stopOnCount || (buffer1 < buffer1End))
+ {
+ CHAR ch1 = *buffer1;
+ CHAR ch2 = *buffer2;
+ diff = ch1 - ch2;
+ if (diff != 0 || stopOnNull)
+ {
+ if (ch1 == 0 || ch2 == 0)
+ {
+ break;
+ }
+ diff = (SIMPLE_UPCASE_ANSI(ch1) - SIMPLE_UPCASE_ANSI(ch2));
+ if (diff != 0)
+ {
+ break;
+ }
+ }
+ buffer1++;
+ buffer2++;
+ }
+ return diff;
+}
+
+
+int CaseHashHelper(const WCHAR *buffer, COUNT_T count)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ const WCHAR *bufferEnd = buffer + count;
+ ULONG hash = 5381;
+
+ while (buffer < bufferEnd)
+ {
+ WCHAR ch = *buffer++;
+ ch = CAN_SIMPLE_UPCASE(ch) ? SIMPLE_UPCASE(ch) : MapChar(ch, LCMAP_UPPERCASE);
+
+ hash = (((hash << 5) + hash) ^ ch);
+ }
+
+ return hash;
+}
+
+static int CaseHashHelperA(const CHAR *buffer, COUNT_T count)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ const CHAR *bufferEnd = buffer + count;
+ ULONG hash = 5381;
+
+ while (buffer < bufferEnd)
+ {
+ CHAR ch = *buffer++;
+ ch = SIMPLE_UPCASE_ANSI(ch);
+
+ hash = (((hash << 5) + hash) ^ ch);
+ }
+
+ return hash;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the unicode string
+//-----------------------------------------------------------------------------
+void SString::Set(const WCHAR *string)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) wcslen(string), REPRESENTATION_UNICODE);
+ wcscpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// unicode string.
+//-----------------------------------------------------------------------------
+void SString::Set(const WCHAR *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_UNICODE);
+ wcsncpy_s(GetRawUnicode(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawUnicode()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a point to the first count characters of the given
+// preallocated unicode string (shallow copy).
+//-----------------------------------------------------------------------------
+void SString::SetPreallocated(const WCHAR *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ SS_POSTCONDITION(IsEmpty());
+ GC_NOTRIGGER;
+ NOTHROW;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ SetImmutable();
+ SetImmutable((BYTE*) string, count*2);
+ ClearAllocated();
+ SetRepresentation(REPRESENTATION_UNICODE);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given ansi string
+//-----------------------------------------------------------------------------
+void SString::SetASCII(const ASCII *string)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_ASCII);
+ strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// ascii string
+//-----------------------------------------------------------------------------
+void SString::SetASCII(const ASCII *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckASCIIString(string, count));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_ASCII);
+ strncpy_s(GetRawASCII(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawASCII()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given UTF8 string
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(const UTF8 *string)
+{
+ SS_CONTRACT_VOID
+ {
+ // !!! Check for illegal UTF8 encoding?
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_UTF8);
+ strcpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// UTF8 string.
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(const UTF8 *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ // !!! Check for illegal UTF8 encoding?
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_UTF8);
+ strncpy_s(GetRawUTF8(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawUTF8()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the given ANSI string
+//-----------------------------------------------------------------------------
+void SString::SetANSI(const ANSI *string)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (string == NULL || *string == 0)
+ Clear();
+ else
+ {
+ Resize((COUNT_T) strlen(string), REPRESENTATION_ANSI);
+ strcpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string);
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to a copy of the first count characters of the given
+// ANSI string.
+//-----------------------------------------------------------------------------
+void SString::SetANSI(const ANSI *string, COUNT_T count)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(string, NULL_OK));
+ PRECONDITION(CheckCount(count));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (count == 0)
+ Clear();
+ else
+ {
+ Resize(count, REPRESENTATION_ANSI);
+ strncpy_s(GetRawANSI(), GetBufferSizeInCharIncludeNullChar(), string, count);
+ GetRawANSI()[count] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given unicode character
+//-----------------------------------------------------------------------------
+void SString::Set(WCHAR character)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ if (character == 0)
+ Clear();
+ else
+ {
+ Resize(1, REPRESENTATION_UNICODE);
+ GetRawUnicode()[0] = character;
+ GetRawUnicode()[1] = 0;
+ }
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given UTF8 character
+//-----------------------------------------------------------------------------
+void SString::SetUTF8(CHAR character)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (character == 0)
+ Clear();
+ else
+ {
+ Resize(1, REPRESENTATION_UTF8);
+ GetRawUTF8()[0] = character;
+ GetRawUTF8()[1] = 0;
+ }
+
+ SS_RETURN;
+}
+
+
+//-----------------------------------------------------------------------------
+// Set this string to the given ansi literal.
+// This will share the memory and not make a copy.
+//-----------------------------------------------------------------------------
+void SString::SetLiteral(const ASCII *literal)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ PRECONDITION(CheckASCIIString(literal));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SString s(Literal, literal);
+ Set(s);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set this string to the given unicode literal.
+// This will share the memory and not make a copy.
+//-----------------------------------------------------------------------------
+void SString::SetLiteral(const WCHAR *literal)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(literal));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ SString s(Literal, literal);
+ Set(s);
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Hash the string contents
+//-----------------------------------------------------------------------------
+ULONG SString::Hash() const
+{
+ SS_CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ SS_RETURN HashString(GetRawUnicode());
+}
+
+//-----------------------------------------------------------------------------
+// Hash the string contents
+//-----------------------------------------------------------------------------
+ULONG SString::HashCaseInsensitive() const
+{
+ SS_CONTRACT(ULONG)
+ {
+ INSTANCE_CHECK;
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToIteratable();
+
+ ULONG result;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_EMPTY:
+ result = CaseHashHelper(GetRawUnicode(), GetRawCount());
+ break;
+
+ case REPRESENTATION_ASCII:
+ result = CaseHashHelperA(GetRawASCII(), GetRawCount());
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ SS_RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Truncate this string to count characters.
+//-----------------------------------------------------------------------------
+void SString::Truncate(const Iterator &i)
+{
+ SS_CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ SS_POSTCONDITION(GetRawCount() == i - Begin());
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ SS_CONTRACT_END;
+
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ COUNT_T size = i - Begin();
+
+ Resize(size, GetRepresentation(), PRESERVE);
+
+ i.Resync(this, (BYTE *) (GetRawUnicode() + size));
+
+ SS_RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the ASCII representation for this String to Unicode. We can do this
+// quickly and in-place (if this == &dest), which is why it is optimized.
+//-----------------------------------------------------------------------------
+void SString::ConvertASCIIToUnicode(SString &dest) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(IsRepresentation(REPRESENTATION_ASCII));
+ POSTCONDITION(dest.IsRepresentation(REPRESENTATION_UNICODE));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ // Handle the empty case.
+ if (IsEmpty())
+ {
+ dest.Clear();
+ RETURN;
+ }
+
+ CONSISTENCY_CHECK(CheckPointer(GetRawASCII()));
+ CONSISTENCY_CHECK(GetRawCount() > 0);
+
+ // If dest is the same as this, then we need to preserve on resize.
+ dest.Resize(GetRawCount(), REPRESENTATION_UNICODE,
+ this == &dest ? PRESERVE : DONT_PRESERVE);
+
+ // Make sure the buffer is big enough.
+ CONSISTENCY_CHECK(dest.GetAllocation() > (GetRawCount() * sizeof(WCHAR)));
+
+ // This is a poor man's widen. Since we know that the representation is ASCII,
+ // we can just pad the string with a bunch of zero-value bytes. Of course,
+ // we move from the end of the string to the start so that we can convert in
+ // place (in the case that &dest == this).
+ WCHAR *outBuf = dest.GetRawUnicode() + dest.GetRawCount();
+ ASCII *inBuf = GetRawASCII() + GetRawCount();
+
+ while (GetRawASCII() <= inBuf)
+ {
+ CONSISTENCY_CHECK(dest.GetRawUnicode() <= outBuf);
+ // The casting zero-extends the value, thus giving us the zero-valued byte.
+ *outBuf = (WCHAR) *inBuf;
+ outBuf--;
+ inBuf--;
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation for this String to Unicode.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode() const
+{
+ CONTRACT_VOID
+ {
+ POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (!IsRepresentation(REPRESENTATION_UNICODE))
+ {
+ if (IsRepresentation(REPRESENTATION_ASCII))
+ {
+ ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
+ }
+ else
+ {
+ StackSString s;
+ ConvertToUnicode(s);
+ PREFIX_ASSUME(!s.IsImmutable());
+ (const_cast<SString*>(this))->Set(s);
+ }
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Convert the internal representation for this String to Unicode, while
+// preserving the iterator if the conversion is done.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode(const CIterator &i) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(i.Check());
+ POSTCONDITION(IsRepresentation(REPRESENTATION_UNICODE));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ if (!IsRepresentation(REPRESENTATION_UNICODE))
+ {
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ COUNT_T index = 0;
+ // Get the current index of the iterator
+ if (i.m_ptr != NULL)
+ {
+ CONSISTENCY_CHECK(GetCharacterSizeShift() == 0);
+ index = (COUNT_T) (i.m_ptr - m_buffer);
+ }
+
+ if (IsRepresentation(REPRESENTATION_ASCII))
+ {
+ ConvertASCIIToUnicode(*(const_cast<SString *>(this)));
+ }
+ else
+ {
+ StackSString s;
+ ConvertToUnicode(s);
+ (const_cast<SString*>(this))->Set(s);
+ }
+
+ // Move the iterator to the new location.
+ if (i.m_ptr != NULL)
+ {
+ i.Resync(this, (BYTE *) (GetRawUnicode() + index));
+ }
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the unicode format.
+//-----------------------------------------------------------------------------
+void SString::ConvertToUnicode(SString &s) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_UNICODE));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ int page = 0;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN;
+
+ case REPRESENTATION_UNICODE:
+ s.Set(*this);
+ RETURN;
+
+ case REPRESENTATION_UTF8:
+ page = CP_UTF8;
+ break;
+
+ case REPRESENTATION_ASCII:
+ ConvertASCIIToUnicode(s);
+ RETURN;
+
+ case REPRESENTATION_ANSI:
+ page = CP_ACP;
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ COUNT_T length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, 0, 0);
+ if (length == 0)
+ ThrowLastError();
+
+ s.Resize(length-1, REPRESENTATION_UNICODE);
+
+ length = WszMultiByteToWideChar(page, 0, GetRawANSI(), GetRawCount()+1, s.GetRawUnicode(), length);
+ if (length == 0)
+ ThrowLastError();
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the ANSI format.
+//-----------------------------------------------------------------------------
+void SString::ConvertToANSI(SString &s) const
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_ANSI));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ s.Set(*this);
+ RETURN;
+
+ case REPRESENTATION_UTF8:
+ // No direct conversion to ANSI
+ ConvertToUnicode();
+ FALLTHROUGH;
+
+ case REPRESENTATION_UNICODE:
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // @todo: use WC_NO_BEST_FIT_CHARS
+ COUNT_T length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
+ NULL, 0, NULL, NULL);
+
+ s.Resize(length-1, REPRESENTATION_ANSI);
+
+ // @todo: use WC_NO_BEST_FIT_CHARS
+ length = WszWideCharToMultiByte(CP_ACP, 0, GetRawUnicode(), GetRawCount()+1,
+ s.GetRawANSI(), length, NULL, NULL);
+ if (length == 0)
+ ThrowLastError();
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Set s to be a copy of this string's contents, but in the utf8 format.
+//-----------------------------------------------------------------------------
+COUNT_T SString::ConvertToUTF8(SString &s) const
+{
+ CONTRACT(COUNT_T)
+ {
+ PRECONDITION(s.Check());
+ POSTCONDITION(s.IsRepresentation(REPRESENTATION_UTF8));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ s.Clear();
+ RETURN 1;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_UTF8:
+ s.Set(*this);
+ RETURN s.GetRawCount()+1;
+
+ case REPRESENTATION_ANSI:
+ // No direct conversion from ANSI to UTF8
+ ConvertToUnicode();
+ FALLTHROUGH;
+
+ case REPRESENTATION_UNICODE:
+ break;
+
+ default:
+ UNREACHABLE();
+ }
+
+ // <TODO> @todo: use WC_NO_BEST_FIT_CHARS </TODO>
+ bool allAscii;
+ DWORD length;
+
+ HRESULT hr = FString::Unicode_Utf8_Length(GetRawUnicode(), & allAscii, & length);
+
+ if (SUCCEEDED(hr))
+ {
+ s.Resize(length, REPRESENTATION_UTF8);
+
+ //FString::Unicode_Utf8 expects an array all the time
+ //we optimize the empty string by replacing it with null for SString above in Resize
+ if (length > 0)
+ {
+ hr = FString::Unicode_Utf8(GetRawUnicode(), allAscii, (LPSTR) s.GetRawUTF8(), length);
+ }
+ }
+
+ IfFailThrow(hr);
+
+ RETURN length + 1;
+}
+
+//-----------------------------------------------------------------------------
+// Replace a single character with another character.
+//-----------------------------------------------------------------------------
+void SString::Replace(const Iterator &i, WCHAR c)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, 1));
+ POSTCONDITION(Match(i, c));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_ASCII) && ((c&~0x7f) == 0))
+ {
+ *(BYTE*)i.m_ptr = (BYTE) c;
+ }
+ else
+ {
+ ConvertToUnicode(i);
+
+ *(USHORT*)i.m_ptr = c;
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Replace the substring specified by position, length with the given string s.
+//-----------------------------------------------------------------------------
+void SString::Replace(const Iterator &i, COUNT_T length, const SString &s)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i, length));
+ PRECONDITION(s.Check());
+ POSTCONDITION(Match(i, s));
+ THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ Representation representation = GetRepresentation();
+ if (representation == REPRESENTATION_EMPTY)
+ {
+ // This special case contains some optimizations (like literal sharing).
+ Set(s);
+ ConvertToIteratable();
+ i.Resync(this, m_buffer);
+ }
+ else
+ {
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T deleteSize = length<<GetCharacterSizeShift();
+ COUNT_T insertSize = source.GetRawCount()<<source.GetCharacterSizeShift();
+
+ SBuffer::Replace(i, deleteSize, insertSize);
+ SBuffer::Copy(i, source.m_buffer, insertSize);
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string starting at i. Return TRUE & update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::Find(CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ POSTCONDITION(RETVAL == Match(i, s));
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ COUNT_T count = source.GetRawCount();
+ const WCHAR *start = i.GetUnicode();
+ const WCHAR *end = GetUnicode() + GetRawCount() - count;
+ while (start <= end)
+ {
+ if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ COUNT_T count = source.GetRawCount();
+ const CHAR *start = i.GetASCII();
+ const CHAR *end = GetRawASCII() + GetRawCount() - count;
+ while (start <= end)
+ {
+ if (strncmp(start, source.GetRawASCII(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ {
+ if (source.GetRawCount() == 0)
+ RETURN TRUE;
+ }
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string starting at i. Return TRUE & update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::Find(CIterator &i, WCHAR c) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ POSTCONDITION(RETVAL == Match(i, c));
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string
+ if (c & ~0x7f)
+ ConvertToUnicode(i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ const WCHAR *start = i.GetUnicode();
+ const WCHAR *end = GetUnicode() + GetRawCount() - 1;
+ while (start <= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ const CHAR *start = i.GetASCII();
+ const CHAR *end = GetRawASCII() + GetRawCount() - 1;
+ while (start <= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start++;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string, working backwards staring at i.
+// Return TRUE and update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::FindBack(CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ POSTCONDITION(RETVAL == Match(i, s));
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ COUNT_T count = source.GetRawCount();
+ const WCHAR *start = GetRawUnicode() + GetRawCount() - count;
+ if (start > i.GetUnicode())
+ start = i.GetUnicode();
+ const WCHAR *end = GetRawUnicode();
+
+ while (start >= end)
+ {
+ if (wcsncmp(start, source.GetRawUnicode(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ COUNT_T count = source.GetRawCount();
+ const CHAR *start = GetRawASCII() + GetRawCount() - count;
+ if (start > i.GetASCII())
+ start = i.GetASCII();
+ const CHAR *end = GetRawASCII();
+
+ while (start >= end)
+ {
+ if (strncmp(start, source.GetRawASCII(), count) == 0)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ {
+ if (source.GetRawCount() == 0)
+ RETURN TRUE;
+ }
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find s in this string, working backwards staring at i.
+// Return TRUE and update iterator if found.
+//-----------------------------------------------------------------------------
+BOOL SString::FindBack(CIterator &i, WCHAR c) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ POSTCONDITION(RETVAL == Match(i, c));
+ THROWS_UNLESS_NORMALIZED;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ // Get a compatible string from s
+ if (c & ~0x7f)
+ ConvertToUnicode(i);
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ {
+ const WCHAR *start = GetRawUnicode() + GetRawCount() - 1;
+ if (start > i.GetUnicode())
+ start = i.GetUnicode();
+ const WCHAR *end = GetRawUnicode();
+
+ while (start >= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_ANSI:
+ case REPRESENTATION_ASCII:
+ {
+ const CHAR *start = GetRawASCII() + GetRawCount() - 1;
+ if (start > i.GetASCII())
+ start = i.GetASCII();
+ const CHAR *end = GetRawASCII();
+
+ while (start >= end)
+ {
+ if (*start == c)
+ {
+ i.Resync(this, (BYTE*) start);
+ RETURN TRUE;
+ }
+ start--;
+ }
+ }
+ break;
+
+ case REPRESENTATION_EMPTY:
+ break;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string begins with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::BeginsWith(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return Match(Begin(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string begins with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::BeginsWithCaseInsensitive(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ return MatchCaseInsensitive(Begin(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string ends with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::EndsWith(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Need this check due to iterator arithmetic below.
+ if (GetCount() < s.GetCount())
+ {
+ return FALSE;
+ }
+
+ return Match(End() - s.GetCount(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Returns TRUE if this string ends with the contents of s
+//-----------------------------------------------------------------------------
+BOOL SString::EndsWithCaseInsensitive(const SString &s) const
+{
+ WRAPPER_NO_CONTRACT;
+
+ // Need this check due to iterator arithmetic below.
+ if (GetCount() < s.GetCount())
+ {
+ return FALSE;
+ }
+
+ return MatchCaseInsensitive(End() - s.GetCount(), s);
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// The comparison does not take into account localization issues like case folding.
+// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
+//-----------------------------------------------------------------------------
+int SString::Compare(const SString &s) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T smaller;
+ int equals = 0;
+ int result = 0;
+
+ if (GetRawCount() < source.GetRawCount())
+ {
+ smaller = GetRawCount();
+ equals = -1;
+ }
+ else if (GetRawCount() > source.GetRawCount())
+ {
+ smaller = source.GetRawCount();
+ equals = 1;
+ }
+ else
+ {
+ smaller = GetRawCount();
+ equals = 0;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ result = wcsncmp(GetRawUnicode(), source.GetRawUnicode(), smaller);
+ break;
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ result = strncmp(GetRawASCII(), source.GetRawASCII(), smaller);
+ break;
+
+ case REPRESENTATION_EMPTY:
+ result = 0;
+ break;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ if (result == 0)
+ RETURN equals;
+ else
+ RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// Return 0 if equal, <0 if this < s, >0 is this > s. (same as strcmp).
+//-----------------------------------------------------------------------------
+
+int SString::CompareCaseInsensitive(const SString &s) const
+{
+ CONTRACT(int)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T smaller;
+ int equals = 0;
+ int result = 0;
+
+ if (GetRawCount() < source.GetRawCount())
+ {
+ smaller = GetRawCount();
+ equals = -1;
+ }
+ else if (GetRawCount() > source.GetRawCount())
+ {
+ smaller = source.GetRawCount();
+ equals = 1;
+ }
+ else
+ {
+ smaller = GetRawCount();
+ equals = 0;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_ANSI:
+ result = CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), smaller, FALSE, TRUE);
+ break;
+
+ case REPRESENTATION_ASCII:
+ result = CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), smaller, FALSE, TRUE);
+ break;
+
+ case REPRESENTATION_EMPTY:
+ result = 0;
+ break;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ if (result == 0)
+ RETURN equals;
+ else
+ RETURN result;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents to s's contents.
+// The comparison does not take into account localization issues like case folding.
+// Return 1 if equal, 0 if not.
+//-----------------------------------------------------------------------------
+BOOL SString::Equals(const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T count = GetRawCount();
+
+ if (count != source.GetRawCount())
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ RETURN (wcsncmp(GetRawUnicode(), source.GetRawUnicode(), count) == 0);
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ RETURN (strncmp(GetRawASCII(), source.GetRawASCII(), count) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare this string's contents case insensitively to s's contents.
+// Return 1 if equal, 0 if not.
+//-----------------------------------------------------------------------------
+BOOL SString::EqualsCaseInsensitive(const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ FAULTS_UNLESS_BOTH_NORMALIZED(s, ThrowOutOfMemory());
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp);
+
+ COUNT_T count = GetRawCount();
+
+ if (count != source.GetRawCount())
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_ANSI:
+ RETURN (CaseCompareHelper(GetRawUnicode(), source.GetRawUnicode(), count, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ RETURN (CaseCompareHelperA(GetRawASCII(), source.GetRawASCII(), count, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare s's contents to the substring starting at position
+// The comparison does not take into account localization issues like case folding.
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::Match(const CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T remaining = End() - i;
+ COUNT_T count = source.GetRawCount();
+
+ if (remaining < count)
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ RETURN (wcsncmp(i.GetUnicode(), source.GetRawUnicode(), count) == 0);
+
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ RETURN (strncmp(i.GetASCII(), source.GetRawASCII(), count) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare s's contents case insensitively to the substring starting at position
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::MatchCaseInsensitive(const CIterator &i, const SString &s) const
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ PRECONDITION(s.Check());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ StackSString temp;
+ const SString &source = GetCompatibleString(s, temp, i);
+
+ COUNT_T remaining = End() - i;
+ COUNT_T count = source.GetRawCount();
+
+ if (remaining < count)
+ RETURN FALSE;
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_ANSI:
+ RETURN (CaseCompareHelper(i.GetUnicode(), source.GetRawUnicode(), count, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_ASCII:
+ RETURN (CaseCompareHelperA(i.GetASCII(), source.GetRawASCII(), count, FALSE, TRUE) == 0);
+
+ case REPRESENTATION_EMPTY:
+ RETURN TRUE;
+
+ default:
+ case REPRESENTATION_UTF8:
+ UNREACHABLE();
+ }
+
+ RETURN FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Compare c case insensitively to the character at position
+// Return TRUE if equal, FALSE if not
+//-----------------------------------------------------------------------------
+BOOL SString::MatchCaseInsensitive(const CIterator &i, WCHAR c) const
+{
+ SS_CONTRACT(BOOL)
+ {
+ GC_NOTRIGGER;
+ INSTANCE_CHECK;
+ PRECONDITION(CheckIteratorRange(i));
+ NOTHROW;
+ }
+ SS_CONTRACT_END;
+
+ // End() will not throw here
+ CONTRACT_VIOLATION(ThrowsViolation);
+ if (i >= End())
+ SS_RETURN FALSE;
+
+ WCHAR test = i[0];
+
+ SS_RETURN (test == c
+ || ((CAN_SIMPLE_UPCASE(test) ? SIMPLE_UPCASE(test) : MapChar(test, LCMAP_UPPERCASE))
+ == (CAN_SIMPLE_UPCASE(c) ? SIMPLE_UPCASE(c) : MapChar(c, LCMAP_UPPERCASE))));
+}
+
+//-----------------------------------------------------------------------------
+// Convert string to unicode lowercase using the invariant culture
+// Note: Please don't use it in PATH as multiple character can map to the same
+// lower case symbol
+//-----------------------------------------------------------------------------
+void SString::LowerCase()
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Convert null-terminated string to lowercase using the invariant culture
+//-----------------------------------------------------------------------------
+//static
+void SString::LowerCase(__inout_z LPWSTR wszString)
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ NOTHROW;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ if (wszString == NULL)
+ {
+ return;
+ }
+
+ for (WCHAR * pwch = wszString; *pwch != '\0'; ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_DOWNCASE(*pwch) ? SIMPLE_DOWNCASE(*pwch) : MapChar(*pwch, LCMAP_LOWERCASE));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Convert string to unicode uppercase using the invariant culture
+// Note: Please don't use it in PATH as multiple character can map to the same
+// upper case symbol
+//-----------------------------------------------------------------------------
+void SString::UpperCase()
+{
+ SS_CONTRACT_VOID
+ {
+ GC_NOTRIGGER;
+ PRECONDITION(CheckPointer(this));
+ SS_POSTCONDITION(CheckPointer(RETVAL));
+ if (IsRepresentation(REPRESENTATION_UNICODE)) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ SS_CONTRACT_END;
+
+ ConvertToUnicode();
+
+ for (WCHAR *pwch = GetRawUnicode(); pwch < GetRawUnicode() + GetRawCount(); ++pwch)
+ {
+ *pwch = (CAN_SIMPLE_UPCASE(*pwch) ? SIMPLE_UPCASE(*pwch) : MapChar(*pwch, LCMAP_UPPERCASE));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer as an ANSI string.
+//-----------------------------------------------------------------------------
+const CHAR *SString::GetANSI(AbstractScratchBuffer &scratch) const
+{
+ SS_CONTRACT(const CHAR *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ SS_CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_ANSI))
+ SS_RETURN GetRawANSI();
+
+ ConvertToANSI((SString&)scratch);
+ SS_RETURN ((SString&)scratch).GetRawANSI();
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer as a UTF8 string.
+//-----------------------------------------------------------------------------
+const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch) const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ RETURN GetRawUTF8();
+
+ ConvertToUTF8((SString&)scratch);
+ RETURN ((SString&)scratch).GetRawUTF8();
+}
+
+const UTF8 *SString::GetUTF8(AbstractScratchBuffer &scratch, COUNT_T *pcbUtf8) const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ {
+ *pcbUtf8 = GetRawCount() + 1;
+ RETURN GetRawUTF8();
+ }
+
+ *pcbUtf8 = ConvertToUTF8((SString&)scratch);
+ RETURN ((SString&)scratch).GetRawUTF8();
+}
+
+//-----------------------------------------------------------------------------
+// Get a const pointer to the internal buffer which must already be a UTF8 string.
+// This avoids the need to create a scratch buffer we know will never be used.
+//-----------------------------------------------------------------------------
+const UTF8 *SString::GetUTF8NoConvert() const
+{
+ CONTRACT(const UTF8 *)
+ {
+ INSTANCE_CHECK_NULL;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (IsRepresentation(REPRESENTATION_UTF8))
+ RETURN GetRawUTF8();
+
+ ThrowHR(E_INVALIDARG);
+}
+
+//-----------------------------------------------------------------------------
+// Safe version of sprintf.
+// Prints formatted ansi text w/ var args to this buffer.
+//-----------------------------------------------------------------------------
+void SString::Printf(const CHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ VPrintf(format, args);
+ va_end(args);
+}
+
+#ifdef _DEBUG
+//
+// Check the Printf use for potential globalization bugs. %S formatting
+// specifier does Unicode->Ansi or Ansi->Unicode conversion using current
+// C-locale. This almost always means globalization bug in the CLR codebase.
+//
+// Ideally, we would elimitate %S from all format strings. Unfortunately,
+// %S is too widespread in non-shipping code that such cleanup is not feasible.
+//
+static void CheckForFormatStringGlobalizationIssues(const SString &format, const SString &result)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_NOTRIGGER;
+ DEBUG_ONLY;
+ }
+ CONTRACTL_END;
+
+ BOOL fDangerousFormat = FALSE;
+
+ // Check whether the format string contains the %S formatting specifier
+ SString::CIterator itrFormat = format.Begin();
+ while (*itrFormat)
+ {
+ if (*itrFormat++ == '%')
+ {
+ // <TODO>Handle the complex format strings like %blahS</TODO>
+ if (*itrFormat++ == 'S')
+ {
+ fDangerousFormat = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (fDangerousFormat)
+ {
+ BOOL fNonAsciiUsed = FALSE;
+
+ // Now check whether there are any non-ASCII characters in the output.
+
+ // Check whether the result contains non-Ascii characters
+ SString::CIterator itrResult = format.Begin();
+ while (*itrResult)
+ {
+ if (*itrResult++ > 127)
+ {
+ fNonAsciiUsed = TRUE;
+ break;
+ }
+ }
+
+ CONSISTENCY_CHECK_MSGF(!fNonAsciiUsed,
+ ("Non-ASCII string was produced by %%S format specifier. This is likely globalization bug."
+ "To fix this, change the format string to %%s and do the correct encoding at the Printf callsite"));
+ }
+}
+#endif
+
+#ifndef EBADF
+#define EBADF 9
+#endif
+
+#ifndef ENOMEM
+#define ENOMEM 12
+#endif
+
+#ifndef ERANGE
+#define ERANGE 34
+#endif
+
+#if defined(_MSC_VER)
+#undef va_copy
+#define va_copy(dest,src) (dest = src)
+#endif
+
+void SString::VPrintf(const CHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list ap;
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+ va_copy(ap, args);
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ if (result >=0)
+ {
+ // Succeeded in writing. Now resize -
+ Resize(result, REPRESENTATION_ANSI, PRESERVE);
+ SString sss(Ansi, format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) strlen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_ANSI);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+ va_copy(ap, args);
+ int result = _vsnprintf_s(GetRawANSI(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ if (result >= 0)
+ {
+ // Succeed in writing. Shrink the buffer to fit exactly.
+ Resize(result, REPRESENTATION_ANSI, PRESERVE);
+ SString sss(Ansi, format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnprintf_s failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::Printf(const WCHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ VPrintf(format, args);
+ va_end(args);
+}
+
+void SString::PPrintf(const WCHAR *format, ...)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list argItr;
+ va_start(argItr, format);
+ PVPrintf(format, argItr);
+ va_end(argItr);
+
+ RETURN;
+}
+
+void SString::VPrintf(const WCHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list ap;
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+ va_copy(ap, args);
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ if (result >= 0)
+ {
+ // succeeded
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) wcslen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_UNICODE);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+ va_copy(ap, args);
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+ va_end(ap);
+
+ if (result >= 0)
+ {
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf_s failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::PVPrintf(const WCHAR *format, va_list args)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ PRECONDITION(CheckPointer(format));
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ va_list ap;
+ // sprintf gives us no means to know how many characters are written
+ // other than guessing and trying
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use the existing buffer
+ va_copy(ap, args);
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
+#endif
+ va_end(ap);
+ if (result >= 0)
+ {
+ // succeeded
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+ }
+
+ // Make a guess how long the result will be (note this will be doubled)
+
+ COUNT_T guess = (COUNT_T) wcslen(format)+1;
+ if (guess < GetRawCount())
+ guess = GetRawCount();
+ if (guess < MINIMUM_GUESS)
+ guess = MINIMUM_GUESS;
+
+ while (TRUE)
+ {
+ // Double the previous guess - eventually we will get enough space
+ guess *= 2;
+ Resize(guess, REPRESENTATION_UNICODE, DONT_PRESERVE);
+
+ // Clear errno to avoid false alarms
+ errno = 0;
+
+ va_copy(ap, args);
+#if defined(FEATURE_CORESYSTEM)
+ int result = _vsnwprintf_s(GetRawUnicode(), GetRawCount()+1, _TRUNCATE, format, ap);
+#else
+ int result = _vswprintf_p(GetRawUnicode(), GetRawCount()+1, format, ap);
+#endif
+ va_end(ap);
+
+ if (result >= 0)
+ {
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ SString sss(format);
+ INDEBUG(CheckForFormatStringGlobalizationIssues(sss, *this));
+ RETURN;
+ }
+
+ if (errno==ENOMEM)
+ {
+ ThrowOutOfMemory();
+ }
+ else
+ if (errno!=0 && errno!=EBADF && errno!=ERANGE)
+ {
+ CONSISTENCY_CHECK_MSG(FALSE, "_vsnwprintf_s failed. Potential globalization bug.");
+ ThrowHR(HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION));
+ }
+ }
+ RETURN;
+}
+
+void SString::AppendPrintf(const CHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ AppendVPrintf(format, args);
+ va_end(args);
+}
+
+void SString::AppendVPrintf(const CHAR *format, va_list args)
+{
+ WRAPPER_NO_CONTRACT;
+
+ StackSString s;
+ s.VPrintf(format, args);
+ Append(s);
+}
+
+void SString::AppendPrintf(const WCHAR *format, ...)
+{
+ WRAPPER_NO_CONTRACT;
+
+ va_list args;
+ va_start(args, format);
+ AppendVPrintf(format, args);
+ va_end(args);
+}
+
+void SString::AppendVPrintf(const WCHAR *format, va_list args)
+{
+ WRAPPER_NO_CONTRACT;
+
+ StackSString s;
+ s.VPrintf(format, args);
+ Append(s);
+}
+
+//----------------------------------------------------------------------------
+// LoadResource - moved to sstring_com.cpp
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+// Format the message and put the contents in this string
+//----------------------------------------------------------------------------
+
+BOOL SString::FormatMessage(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId,
+ const SString &arg1, const SString &arg2,
+ const SString &arg3, const SString &arg4,
+ const SString &arg5, const SString &arg6,
+ const SString &arg7, const SString &arg8,
+ const SString &arg9, const SString &arg10)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ const WCHAR *args[] = {arg1.GetUnicode(), arg2.GetUnicode(), arg3.GetUnicode(), arg4.GetUnicode(),
+ arg5.GetUnicode(), arg6.GetUnicode(), arg7.GetUnicode(), arg8.GetUnicode(),
+ arg9.GetUnicode(), arg10.GetUnicode()};
+
+ if (GetRawCount() > 0)
+ {
+ // First, try to use our existing buffer to hold the result.
+ Resize(GetRawCount(), REPRESENTATION_UNICODE);
+
+ DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource, dwMessageId, dwLanguageId,
+ GetRawUnicode(), GetRawCount()+1, (va_list*)args);
+
+ // Although we cannot directly detect truncation, we can tell if we
+ // used up all the space (in which case we will assume truncation.)
+
+ if (result != 0 && result < GetRawCount())
+ {
+ if (GetRawUnicode()[result-1] == W(' '))
+ {
+ GetRawUnicode()[result-1] = W('\0');
+ result -= 1;
+ }
+ Resize(result, REPRESENTATION_UNICODE, PRESERVE);
+ RETURN TRUE;
+ }
+ }
+
+ // We don't have enough space in our buffer, do dynamic allocation.
+ LocalAllocHolder<WCHAR> string;
+
+ DWORD result = ::WszFormatMessage(dwFlags | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ lpSource, dwMessageId, dwLanguageId,
+ (LPWSTR)(LPWSTR*)&string, 0, (va_list*)args);
+
+ if (result == 0)
+ RETURN FALSE;
+ else
+ {
+ if (string[result-1] == W(' '))
+ string[result-1] = W('\0');
+
+ Set(string);
+ RETURN TRUE;
+ }
+}
+
+#if 1
+//----------------------------------------------------------------------------
+// Helper
+//----------------------------------------------------------------------------
+
+// @todo -this should be removed and placed outside of SString
+void SString::MakeFullNamespacePath(const SString &nameSpace, const SString &name)
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ THROWS;
+ GC_NOTRIGGER;
+ }
+ CONTRACT_END;
+
+ if (nameSpace.GetRepresentation() == REPRESENTATION_UTF8
+ && name.GetRepresentation() == REPRESENTATION_UTF8)
+ {
+ const UTF8 *ns = nameSpace.GetRawUTF8();
+ const UTF8 *n = name.GetRawUTF8();
+ COUNT_T count = ns::GetFullLength(ns, n)-1;
+ Resize(count, REPRESENTATION_UTF8);
+ if (count > 0)
+ ns::MakePath(GetRawUTF8(), count+1, ns, n);
+ }
+ else
+ {
+ const WCHAR *ns = nameSpace;
+ const WCHAR *n = name;
+ COUNT_T count = ns::GetFullLength(ns, n)-1;
+ Resize(count, REPRESENTATION_UNICODE);
+ if (count > 0)
+ ns::MakePath(GetRawUnicode(), count+1, ns, n);
+ }
+
+ RETURN;
+}
+#endif
+
+
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Check to see if the string fits the suggested representation
+//----------------------------------------------------------------------------
+BOOL SString::IsRepresentation(Representation representation) const
+{
+ CONTRACT(BOOL)
+ {
+ PRECONDITION(CheckRepresentation(representation));
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ Representation currentRepresentation = GetRepresentation();
+
+ // If representations are the same, cool.
+ if (currentRepresentation == representation)
+ RETURN TRUE;
+
+ // If we have an empty representation, we match everything
+ if (currentRepresentation == REPRESENTATION_EMPTY)
+ RETURN TRUE;
+
+ // If we're a 1 byte charset, there are some more chances to match
+ if (currentRepresentation != REPRESENTATION_UNICODE
+ && representation != REPRESENTATION_UNICODE)
+ {
+ // If we're ASCII, we can be any 1 byte rep
+ if (currentRepresentation == REPRESENTATION_ASCII)
+ RETURN TRUE;
+
+ // We really want to be ASCII - scan to see if we qualify
+ if (ScanASCII())
+ RETURN TRUE;
+ }
+
+ // Sorry, must convert.
+ RETURN FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Get the contents of the given string in a form which is compatible with our
+// string (and is in a fixed character set.) Updates the given iterator
+// if necessary to keep it in sync.
+//----------------------------------------------------------------------------
+const SString &SString::GetCompatibleString(const SString &s, SString &scratch, const CIterator &i) const
+{
+ CONTRACTL
+ {
+ PRECONDITION(s.Check());
+ PRECONDITION(scratch.Check());
+ PRECONDITION(scratch.CheckEmpty());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACTL_END;
+
+ // Since we have an iterator, we should be fixed size already
+ CONSISTENCY_CHECK(IsFixedSize());
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return s;
+
+ case REPRESENTATION_ASCII:
+ if (s.IsRepresentation(REPRESENTATION_ASCII))
+ return s;
+
+ // We can't in general convert to ASCII, so try unicode.
+ ConvertToUnicode(i);
+ FALLTHROUGH;
+
+ case REPRESENTATION_UNICODE:
+ if (s.IsRepresentation(REPRESENTATION_UNICODE))
+ return s;
+
+ // @todo: we could convert s to unicode - is that a good policy????
+ s.ConvertToUnicode(scratch);
+ return scratch;
+
+ case REPRESENTATION_UTF8:
+ case REPRESENTATION_ANSI:
+ // These should all be impossible since we have an CIterator on us.
+ default:
+ UNREACHABLE_MSG("Unexpected string representation");
+ }
+
+ return s;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Get the contents of the given string in a form which is compatible with our
+// string (and is in a fixed character set.)
+// May convert our string to unicode.
+//----------------------------------------------------------------------------
+const SString &SString::GetCompatibleString(const SString &s, SString &scratch) const
+{
+ CONTRACTL
+ {
+ PRECONDITION(s.Check());
+ PRECONDITION(scratch.Check());
+ PRECONDITION(scratch.CheckEmpty());
+ THROWS_UNLESS_BOTH_NORMALIZED(s);
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // First, make sure we have a fixed size.
+ ConvertToFixed();
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return s;
+
+ case REPRESENTATION_ANSI:
+ if (s.IsRepresentation(REPRESENTATION_ANSI))
+ return s;
+
+ s.ConvertToANSI(scratch);
+ return scratch;
+
+ case REPRESENTATION_ASCII:
+ if (s.IsRepresentation(REPRESENTATION_ASCII))
+ return s;
+
+ // We can't in general convert to ASCII, so try unicode.
+ ConvertToUnicode();
+ FALLTHROUGH;
+
+ case REPRESENTATION_UNICODE:
+ if (s.IsRepresentation(REPRESENTATION_UNICODE))
+ return s;
+
+ // @todo: we could convert s to unicode in place - is that a good policy????
+ s.ConvertToUnicode(scratch);
+ return scratch;
+
+ case REPRESENTATION_UTF8:
+ default:
+ UNREACHABLE();
+ }
+
+ return s;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// If we have a 1 byte representation, scan the buffer to see if we can gain
+// some conversion flexibility by labelling it ASCII
+//----------------------------------------------------------------------------
+BOOL SString::ScanASCII() const
+{
+ CONTRACT(BOOL)
+ {
+ POSTCONDITION(IsRepresentation(REPRESENTATION_ASCII) || IsASCIIScanned());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC;
+ }
+ CONTRACT_END;
+
+ if (!IsASCIIScanned())
+ {
+ const CHAR *c = GetRawANSI();
+ const CHAR *cEnd = c + GetRawCount();
+ while (c < cEnd)
+ {
+ if (*c & 0x80)
+ break;
+ c++;
+ }
+ if (c == cEnd)
+ {
+ const_cast<SString *>(this)->SetRepresentation(REPRESENTATION_ASCII);
+ RETURN TRUE;
+ }
+ else
+ const_cast<SString *>(this)->SetASCIIScanned();
+ }
+ RETURN FALSE;
+}
+
+//----------------------------------------------------------------------------
+// Private helper.
+// Resize updates the geometry of the string and ensures that
+// the space can be written to.
+// count - number of characters (not including null) to hold
+// preserve - if we realloc, do we copy data from old to new?
+//----------------------------------------------------------------------------
+
+void SString::Resize(COUNT_T count, SString::Representation representation, Preserve preserve)
+{
+ CONTRACT_VOID
+ {
+ PRECONDITION(CountToSize(count) >= count);
+ POSTCONDITION(IsRepresentation(representation));
+ POSTCONDITION(GetRawCount() == count);
+ if (count == 0) NOTHROW; else THROWS;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ // If we are resizing to zero, Clear is more efficient
+ if (count == 0)
+ {
+ Clear();
+ }
+ else
+ {
+ SetRepresentation(representation);
+
+ COUNT_T size = CountToSize(count);
+
+ // detect overflow
+ if (size < count)
+ ThrowOutOfMemory();
+
+ ClearNormalized();
+
+ SBuffer::Resize(size, preserve);
+
+ if (IsImmutable())
+ EnsureMutable();
+
+ NullTerminate();
+ }
+
+ RETURN;
+}
+
+//-----------------------------------------------------------------------------
+// This is essentially a specialized version of the above for size 0
+//-----------------------------------------------------------------------------
+void SString::Clear()
+{
+ CONTRACT_VOID
+ {
+ INSTANCE_CHECK;
+ POSTCONDITION(IsEmpty());
+ NOTHROW;
+ GC_NOTRIGGER;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACT_END;
+
+ SetRepresentation(REPRESENTATION_EMPTY);
+
+ if (IsImmutable())
+ {
+ // Use shared empty string rather than allocating a new buffer
+ SBuffer::SetImmutable(s_EmptyBuffer, sizeof(s_EmptyBuffer));
+ }
+ else
+ {
+ // Leave allocated buffer for future growth
+ SBuffer::TweakSize(sizeof(WCHAR));
+ GetRawUnicode()[0] = 0;
+ }
+
+ RETURN;
+}
+
+
+#ifdef DACCESS_COMPILE
+
+//---------------------------------------------------------------------------------------
+//
+// Return a pointer to the raw buffer
+//
+// Returns:
+// A pointer to the raw string buffer.
+//
+void * SString::DacGetRawContent() const
+{
+ if (IsEmpty())
+ {
+ return NULL;
+ }
+
+ switch (GetRepresentation())
+ {
+ case REPRESENTATION_EMPTY:
+ return NULL;
+
+ case REPRESENTATION_UNICODE:
+ case REPRESENTATION_UTF8:
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ // Note: no need to call DacInstantiateString because we know the exact length already.
+ return SBuffer::DacGetRawContent();
+
+ default:
+ DacNotImpl();
+ return NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Return a pointer to the raw buffer as a pointer to a unicode string. Does not
+// do conversion, and thus requires that the representation already be in unicode.
+//
+// Returns:
+// A pointer to the raw string buffer as a unicode string.
+//
+const WCHAR * SString::DacGetRawUnicode() const
+{
+ if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
+ {
+ return W("");
+ }
+
+ if (GetRepresentation() != REPRESENTATION_UNICODE)
+ {
+ DacError(E_UNEXPECTED);
+ }
+
+ HRESULT status = S_OK;
+ WCHAR* wszBuf = NULL;
+ EX_TRY
+ {
+ wszBuf = static_cast<WCHAR*>(SBuffer::DacGetRawContent());
+ }
+ EX_CATCH_HRESULT(status);
+
+ if (SUCCEEDED(status))
+ {
+ return wszBuf;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Copy the string from the target into the provided buffer, converting to unicode if necessary
+//
+// Arguments:
+// cBufChars - size of pBuffer in count of unicode characters.
+// pBuffer - a buffer of cBufChars unicode chars.
+// pcNeedChars - space to store the number of unicode chars in the SString.
+//
+// Returns:
+// true if successful - and buffer is filled with the unicode representation of
+// the string.
+// false if unsuccessful.
+//
+bool SString::DacGetUnicode(COUNT_T cBufChars,
+ __out_z __inout_ecount(cBufChars) WCHAR * pBuffer,
+ COUNT_T * pcNeedChars) const
+{
+ SUPPORTS_DAC;
+
+ PVOID pContent = NULL;
+ int iPage = CP_ACP;
+
+ if (IsEmpty() || (GetRepresentation() == REPRESENTATION_EMPTY))
+ {
+ if (pcNeedChars)
+ {
+ *pcNeedChars = 1;
+ }
+ if (pBuffer && cBufChars)
+ {
+ pBuffer[0] = 0;
+ }
+ return true;
+ }
+
+ HRESULT status = S_OK;
+ EX_TRY
+ {
+ pContent = SBuffer::DacGetRawContent();
+ }
+ EX_CATCH_HRESULT(status);
+
+ if (SUCCEEDED(status) && pContent != NULL)
+ {
+ switch (GetRepresentation())
+ {
+
+ case REPRESENTATION_UNICODE:
+
+ if (pcNeedChars)
+ {
+ *pcNeedChars = GetCount() + 1;
+ }
+
+ if (pBuffer && cBufChars)
+ {
+ if (cBufChars > GetCount() + 1)
+ {
+ cBufChars = GetCount() + 1;
+ }
+ memcpy(pBuffer, pContent, cBufChars * sizeof(*pBuffer));
+ pBuffer[cBufChars - 1] = 0;
+ }
+
+ return true;
+
+ case REPRESENTATION_UTF8:
+ iPage = CP_UTF8;
+ FALLTHROUGH;
+ case REPRESENTATION_ASCII:
+ case REPRESENTATION_ANSI:
+ // iPage defaults to CP_ACP.
+ if (pcNeedChars)
+ {
+ *pcNeedChars = WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, NULL, 0);
+ }
+ if (pBuffer && cBufChars)
+ {
+ if (!WszMultiByteToWideChar(iPage, 0, reinterpret_cast<PSTR>(pContent), -1, pBuffer, cBufChars))
+ {
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ DacNotImpl();
+ return false;
+ }
+ }
+ return false;
+}
+
+#endif //DACCESS_COMPILE
diff --git a/src/coreclr/utilcode/sstring_com.cpp b/src/coreclr/utilcode/sstring_com.cpp
new file mode 100644
index 00000000000..396ec95d735
--- /dev/null
+++ b/src/coreclr/utilcode/sstring_com.cpp
@@ -0,0 +1,101 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// ---------------------------------------------------------------------------
+// SString_COM.cpp
+
+// ---------------------------------------------------------------------------
+
+#include "stdafx.h"
+#include "sstring.h"
+#include "ex.h"
+#include "holder.h"
+
+#define DEFAULT_RESOURCE_STRING_SIZE 255
+
+//----------------------------------------------------------------------------
+// Load the string resource into this string.
+//----------------------------------------------------------------------------
+BOOL SString::LoadResource(CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ return SUCCEEDED(LoadResourceAndReturnHR(eCategory, resourceID));
+}
+
+HRESULT SString::LoadResourceAndReturnHR(CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ WRAPPER_NO_CONTRACT;
+ return LoadResourceAndReturnHR(NULL, eCategory,resourceID);
+}
+
+HRESULT SString::LoadResourceAndReturnHR(CCompRC* pResourceDLL, CCompRC::ResourceCategory eCategory, int resourceID)
+{
+ CONTRACT(BOOL)
+ {
+ INSTANCE_CHECK;
+ NOTHROW;
+ }
+ CONTRACT_END;
+
+ HRESULT hr = E_FAIL;
+
+#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
+ if (pResourceDLL == NULL)
+ {
+ pResourceDLL = CCompRC::GetDefaultResourceDll();
+ }
+
+ if (pResourceDLL != NULL)
+ {
+
+ int size = 0;
+
+ EX_TRY
+ {
+ if (GetRawCount() == 0)
+ Resize(DEFAULT_RESOURCE_STRING_SIZE, REPRESENTATION_UNICODE);
+
+ while (TRUE)
+ {
+ // First try and load the string in the amount of space that we have.
+ // In fatal error reporting scenarios, we may not have enough memory to
+ // allocate a larger buffer.
+
+ hr = pResourceDLL->LoadString(eCategory, resourceID, GetRawUnicode(), GetRawCount()+1, &size);
+ if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ if (FAILED(hr))
+ {
+ Clear();
+ break;
+ }
+
+ // Although we cannot generally detect truncation, we can tell if we
+ // used up all the space (in which case we will assume truncation.)
+ if (size < (int)GetRawCount())
+ {
+ break;
+ }
+ }
+
+ // Double the size and try again.
+ Resize(size*2, REPRESENTATION_UNICODE);
+
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ Truncate(Begin() + (COUNT_T) wcslen(GetRawUnicode()));
+ }
+
+ Normalize();
+
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+ }
+#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
+
+ RETURN hr;
+} // SString::LoadResourceAndReturnHR
diff --git a/src/coreclr/utilcode/stacktrace.cpp b/src/coreclr/utilcode/stacktrace.cpp
new file mode 100644
index 00000000000..9cab6ee2d1f
--- /dev/null
+++ b/src/coreclr/utilcode/stacktrace.cpp
@@ -0,0 +1,991 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+
+#include "stdafx.h"
+
+#include "stacktrace.h"
+#include <imagehlp.h>
+#include "corhlpr.h"
+#include "utilcode.h"
+#include "pedecoder.h" // for IMAGE_FILE_MACHINE_NATIVE
+
+//This is a workaround. We need to work with the debugger team to figure
+//out how the module handle of the CLR can be found in a SxS safe way.
+HMODULE GetCLRModuleHack()
+{
+ static HMODULE s_hModCLR = 0;
+ if (!s_hModCLR)
+ {
+ s_hModCLR = GetModuleHandleA(MAIN_CLR_DLL_NAME_A);
+ }
+ return s_hModCLR;
+}
+
+HINSTANCE LoadImageHlp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+ return LoadLibraryExA("imagehlp.dll", NULL, 0);
+}
+
+HINSTANCE LoadDbgHelp()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+ return LoadLibraryExA("dbghelp.dll", NULL, 0);
+}
+
+/****************************************************************************
+* SymCallback *
+*---------------------*
+* Description:
+* Callback for imghelp.
+****************************************************************************/
+BOOL __stdcall SymCallback
+(
+HANDLE hProcess,
+ULONG ActionCode,
+PVOID CallbackData,
+PVOID UserContext
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ switch (ActionCode)
+ {
+ case CBA_DEBUG_INFO:
+ OutputDebugStringA("IMGHLP: ");
+ OutputDebugStringA((LPCSTR) CallbackData);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_START:
+ OutputDebugStringA("IMGHLP: Deferred symbol load start ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
+ OutputDebugStringA("IMGHLP: Deferred symbol load complete ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
+ OutputDebugStringA("IMGHLP: Deferred symbol load failure ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+
+ case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL:
+ OutputDebugStringA("IMGHLP: Deferred symbol load partial ");
+ OutputDebugStringA(((IMAGEHLP_DEFERRED_SYMBOL_LOAD*)CallbackData)->FileName);
+ OutputDebugStringA("\n");
+ break;
+ }
+
+ return FALSE;
+}
+
+// @TODO_IA64: all of this stack trace stuff is pretty much broken on 64-bit
+// right now because this code doesn't use the new SymXxxx64 functions.
+
+#define LOCAL_ASSERT(x)
+//
+//--- Macros ------------------------------------------------------------------
+//
+
+#define COUNT_OF(x) (sizeof(x) / sizeof(x[0]))
+
+//
+// Types and Constants --------------------------------------------------------
+//
+
+struct SYM_INFO
+{
+ DWORD_PTR dwOffset;
+ char achModule[cchMaxAssertModuleLen];
+ char achSymbol[cchMaxAssertSymbolLen];
+};
+
+//--- Function Pointers to APIs in IMAGEHLP.DLL. Loaded dynamically. ---------
+
+typedef LPAPI_VERSION (__stdcall *pfnImgHlp_ImagehlpApiVersionEx)(
+ LPAPI_VERSION AppVersion
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_StackWalk)(
+ DWORD MachineType,
+ HANDLE hProcess,
+ HANDLE hThread,
+ LPSTACKFRAME StackFrame,
+ LPVOID ContextRecord,
+ PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
+ PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
+ PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
+ PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
+ );
+
+#ifdef HOST_64BIT
+typedef DWORD64 (__stdcall *pfnImgHlp_SymGetModuleBase64)(
+ IN HANDLE hProcess,
+ IN DWORD64 dwAddr
+ );
+
+typedef IMAGEHLP_SYMBOL64 PLAT_IMAGEHLP_SYMBOL;
+typedef IMAGEHLP_MODULE64 PLAT_IMAGEHLP_MODULE;
+
+#else
+typedef IMAGEHLP_SYMBOL PLAT_IMAGEHLP_SYMBOL;
+typedef IMAGEHLP_MODULE PLAT_IMAGEHLP_MODULE;
+#endif
+
+#undef IMAGEHLP_SYMBOL
+#undef IMAGEHLP_MODULE
+
+
+typedef BOOL (__stdcall *pfnImgHlp_SymGetModuleInfo)(
+ IN HANDLE hProcess,
+ IN DWORD_PTR dwAddr,
+ OUT PLAT_IMAGEHLP_MODULE* ModuleInfo
+ );
+
+typedef LPVOID (__stdcall *pfnImgHlp_SymFunctionTableAccess)(
+ HANDLE hProcess,
+ DWORD_PTR AddrBase
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymGetSymFromAddr)(
+ IN HANDLE hProcess,
+ IN DWORD_PTR dwAddr,
+ OUT DWORD_PTR* pdwDisplacement,
+ OUT PLAT_IMAGEHLP_SYMBOL* Symbol
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymInitialize)(
+ IN HANDLE hProcess,
+ IN LPSTR UserSearchPath,
+ IN BOOL fInvadeProcess
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymUnDName)(
+ IN PLAT_IMAGEHLP_SYMBOL* sym, // Symbol to undecorate
+ OUT LPSTR UnDecName, // Buffer to store undecorated name in
+ IN DWORD UnDecNameLength // Size of the buffer
+ );
+
+typedef BOOL (__stdcall *pfnImgHlp_SymLoadModule)(
+ IN HANDLE hProcess,
+ IN HANDLE hFile,
+ IN PSTR ImageName,
+ IN PSTR ModuleName,
+ IN DWORD_PTR BaseOfDll,
+ IN DWORD SizeOfDll
+ );
+
+typedef BOOL (_stdcall *pfnImgHlp_SymRegisterCallback)(
+ IN HANDLE hProcess,
+ IN PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
+ IN PVOID UserContext
+ );
+
+typedef DWORD (_stdcall *pfnImgHlp_SymSetOptions)(
+ IN DWORD SymOptions
+ );
+
+typedef DWORD (_stdcall *pfnImgHlp_SymGetOptions)(
+ );
+
+
+struct IMGHLPFN_LOAD
+{
+ LPCSTR pszFnName;
+ LPVOID * ppvfn;
+};
+
+
+#if defined(HOST_64BIT)
+typedef void (*pfn_GetRuntimeStackWalkInfo)(
+ IN ULONG64 ControlPc,
+ OUT UINT_PTR* pModuleBase,
+ OUT UINT_PTR* pFuncEntry
+ );
+#endif // HOST_64BIT
+
+
+//
+// Globals --------------------------------------------------------------------
+//
+
+static BOOL g_fLoadedImageHlp = FALSE; // set to true on success
+static BOOL g_fLoadedImageHlpFailed = FALSE; // set to true on failure
+static HINSTANCE g_hinstImageHlp = NULL;
+static HINSTANCE g_hinstDbgHelp = NULL;
+static HANDLE g_hProcess = NULL;
+
+pfnImgHlp_ImagehlpApiVersionEx _ImagehlpApiVersionEx;
+pfnImgHlp_StackWalk _StackWalk;
+pfnImgHlp_SymGetModuleInfo _SymGetModuleInfo;
+pfnImgHlp_SymFunctionTableAccess _SymFunctionTableAccess;
+pfnImgHlp_SymGetSymFromAddr _SymGetSymFromAddr;
+pfnImgHlp_SymInitialize _SymInitialize;
+pfnImgHlp_SymUnDName _SymUnDName;
+pfnImgHlp_SymLoadModule _SymLoadModule;
+pfnImgHlp_SymRegisterCallback _SymRegisterCallback;
+pfnImgHlp_SymSetOptions _SymSetOptions;
+pfnImgHlp_SymGetOptions _SymGetOptions;
+#if defined(HOST_64BIT)
+pfn_GetRuntimeStackWalkInfo _GetRuntimeStackWalkInfo;
+#endif // HOST_64BIT
+
+IMGHLPFN_LOAD ailFuncList[] =
+{
+ { "ImagehlpApiVersionEx", (LPVOID*)&_ImagehlpApiVersionEx },
+ { "StackWalk", (LPVOID*)&_StackWalk },
+ { "SymGetModuleInfo", (LPVOID*)&_SymGetModuleInfo },
+ { "SymFunctionTableAccess", (LPVOID*)&_SymFunctionTableAccess },
+ { "SymGetSymFromAddr", (LPVOID*)&_SymGetSymFromAddr },
+ { "SymInitialize", (LPVOID*)&_SymInitialize },
+ { "SymUnDName", (LPVOID*)&_SymUnDName },
+ { "SymLoadModule", (LPVOID*)&_SymLoadModule },
+ { "SymRegisterCallback", (LPVOID*)&_SymRegisterCallback },
+ { "SymSetOptions", (LPVOID*)&_SymSetOptions },
+ { "SymGetOptions", (LPVOID*)&_SymGetOptions },
+};
+
+
+/****************************************************************************
+* FillSymbolSearchPath *
+*----------------------*
+* Description:
+* Manually pick out all the symbol path information we need for a real
+* stack trace to work. This includes the default NT symbol paths and
+* places on a VBL build machine where they should live.
+****************************************************************************/
+#define MAX_SYM_PATH (1024*8)
+#define DEFAULT_SYM_PATH W("symsrv*symsrv.dll*\\\\symbols\\symbols;")
+#define STR_ENGINE_NAME MAIN_CLR_DLL_NAME_W
+LPSTR FillSymbolSearchPathThrows(CQuickBytes &qb)
+{
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+
+#ifndef DACCESS_COMPILE
+ // not allowed to do allocation if current thread suspends EE.
+ if (IsSuspendEEThread ())
+ return NULL;
+#endif
+
+ InlineSString<MAX_SYM_PATH> rcBuff ; // Working buffer
+ int chTotal = 0; // How full is working buffer.
+ int ch;
+
+ // If the NT symbol server path vars are there, then use those.
+ chTotal = WszGetEnvironmentVariable(W("_NT_SYMBOL_PATH"), rcBuff);
+ if (chTotal + 1 < MAX_SYM_PATH)
+ rcBuff.Append(W(';'));
+
+ // Copy the defacto NT symbol path as well.
+ size_t sympathLength = chTotal + NumItems(DEFAULT_SYM_PATH) + 1;
+ // integer overflow occurred
+ if (sympathLength < (size_t)chTotal || sympathLength < NumItems(DEFAULT_SYM_PATH))
+ {
+ return NULL;
+ }
+
+ if (sympathLength < MAX_SYM_PATH)
+ {
+ rcBuff.Append(DEFAULT_SYM_PATH);
+ chTotal = rcBuff.GetCount();
+ }
+
+ // Next, if there is a URTTARGET, add that since that is where ndpsetup places
+ // your symobls on an install.
+ PathString rcBuffTemp;
+ ch = WszGetEnvironmentVariable(W("URTTARGET"), rcBuffTemp);
+ rcBuff.Append(rcBuffTemp);
+ if (ch != 0 && (chTotal + ch + 1 < MAX_SYM_PATH))
+ {
+ size_t chNewTotal = chTotal + ch;
+ if (chNewTotal < (size_t)chTotal || chNewTotal < (size_t)ch)
+ { // integer overflow occurred
+ return NULL;
+ }
+ chTotal += ch;
+ rcBuff.Append(W(';'));
+ }
+
+#ifndef SELF_NO_HOST
+ // Fetch the path location of the engine dll and add that path as well, just
+ // in case URTARGET didn't cut it either.
+ // For no-host builds of utilcode, we don't necessarily have an engine DLL in the
+ // process, so skip this part.
+
+ ch = WszGetModuleFileName(GetCLRModuleHack(), rcBuffTemp);
+
+
+ size_t pathLocationLength = chTotal + ch + 1;
+ // integer overflow occurred
+ if (pathLocationLength < (size_t)chTotal || pathLocationLength < (size_t)ch)
+ {
+ return NULL;
+ }
+
+ if (ch != 0 && (pathLocationLength < MAX_SYM_PATH))
+ {
+ chTotal = chTotal + ch - NumItems(STR_ENGINE_NAME);
+ rcBuff.Append(W(';'));
+ }
+#endif
+
+ // Now we have a working buffer with a bunch of interesting stuff. Time
+ // to convert it back to ansi for the imagehlp api's. Allocate the buffer
+ // 2x bigger to handle worst case for MBCS.
+ ch = ::WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, 0, 0, 0, 0);
+ LPSTR szRtn = (LPSTR) qb.AllocNoThrow(ch + 1);
+ if (!szRtn)
+ return NULL;
+ WszWideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, rcBuff, -1, szRtn, ch+1, 0, 0);
+ return (szRtn);
+}
+LPSTR FillSymbolSearchPath(CQuickBytes &qb)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+ SCAN_IGNORE_FAULT; // Faults from Wsz funcs are handled.
+ LPSTR retval;
+ HRESULT hr = S_OK;
+
+ EX_TRY
+ {
+ retval = FillSymbolSearchPathThrows(qb);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (hr != S_OK)
+ {
+ SetLastError(hr);
+ retval = NULL;
+ }
+
+ return retval;
+}
+
+/****************************************************************************
+* MagicInit *
+*-----------*
+* Description:
+* Initializes the symbol loading code. Currently called (if necessary)
+* at the beginning of each method that might need ImageHelp to be
+* loaded.
+****************************************************************************/
+void MagicInit()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ if (g_fLoadedImageHlp || g_fLoadedImageHlpFailed)
+ {
+ return;
+ }
+
+ g_hProcess = GetCurrentProcess();
+
+ if (g_hinstDbgHelp == NULL)
+ {
+ g_hinstDbgHelp = LoadDbgHelp();
+ }
+ if (NULL == g_hinstDbgHelp)
+ {
+ // Imagehlp.dll has dependency on dbghelp.dll through delay load.
+ // If dbghelp.dll is not available, Imagehlp.dll initializes API's like ImageApiVersionEx to
+ // some dummy function. Then we AV when we use data from _ImagehlpApiVersionEx
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ //
+ // Try to load imagehlp.dll
+ //
+ if (g_hinstImageHlp == NULL) {
+ g_hinstImageHlp = LoadImageHlp();
+ }
+ LOCAL_ASSERT(g_hinstImageHlp);
+
+ if (NULL == g_hinstImageHlp)
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ //
+ // Try to get the API entrypoints in imagehlp.dll
+ //
+ for (int i = 0; i < COUNT_OF(ailFuncList); i++)
+ {
+ *(ailFuncList[i].ppvfn) = GetProcAddress(
+ g_hinstImageHlp,
+ ailFuncList[i].pszFnName);
+ LOCAL_ASSERT(*(ailFuncList[i].ppvfn));
+
+ if (!*(ailFuncList[i].ppvfn))
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+ }
+
+ API_VERSION AppVersion = { 4, 0, API_VERSION_NUMBER, 0 };
+ LPAPI_VERSION papiver = _ImagehlpApiVersionEx(&AppVersion);
+
+ //
+ // We assume any version 4 or greater is OK.
+ //
+ LOCAL_ASSERT(papiver->Revision >= 4);
+ if (papiver->Revision < 4)
+ {
+ g_fLoadedImageHlpFailed = TRUE;
+ return;
+ }
+
+ g_fLoadedImageHlp = TRUE;
+
+ //
+ // Initialize imagehlp.dll. A NULL search path is supposed to resolve
+ // symbols but never works. So pull in everything and put some additional
+ // hints that might help out a dev box.
+ //
+
+ _SymSetOptions(_SymGetOptions() | SYMOPT_DEFERRED_LOADS|SYMOPT_DEBUG);
+#ifndef HOST_64BIT
+ _SymRegisterCallback(g_hProcess, SymCallback, 0);
+#endif
+
+ CQuickBytes qbSearchPath;
+ LPSTR szSearchPath = FillSymbolSearchPath(qbSearchPath);
+ _SymInitialize(g_hProcess, szSearchPath, TRUE);
+
+ return;
+}
+
+
+/****************************************************************************
+* FillSymbolInfo *
+*----------------*
+* Description:
+* Fills in a SYM_INFO structure
+****************************************************************************/
+void FillSymbolInfo
+(
+SYM_INFO *psi,
+DWORD_PTR dwAddr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ if (!g_fLoadedImageHlp)
+ {
+ return;
+ }
+
+ LOCAL_ASSERT(psi);
+ memset(psi, 0, sizeof(SYM_INFO));
+
+ PLAT_IMAGEHLP_MODULE mi;
+ mi.SizeOfStruct = sizeof(mi);
+
+ if (!_SymGetModuleInfo(g_hProcess, dwAddr, &mi))
+ {
+ strcpy_s(psi->achModule, _countof(psi->achModule), "<no module>");
+ }
+ else
+ {
+ strcpy_s(psi->achModule, _countof(psi->achModule), mi.ModuleName);
+ _strupr_s(psi->achModule, _countof(psi->achModule));
+ }
+
+ CHAR rgchUndec[256];
+ const CHAR * pszSymbol = NULL;
+
+ // Name field of IMAGEHLP_SYMBOL is dynamically sized.
+ // Pad with space for 255 characters.
+ union
+ {
+ CHAR rgchSymbol[sizeof(PLAT_IMAGEHLP_SYMBOL) + 255];
+ PLAT_IMAGEHLP_SYMBOL sym;
+ };
+
+ __try
+ {
+ sym.SizeOfStruct = sizeof(PLAT_IMAGEHLP_SYMBOL);
+ sym.Address = dwAddr;
+ sym.MaxNameLength = 255;
+
+ if (_SymGetSymFromAddr(g_hProcess, dwAddr, &psi->dwOffset, &sym))
+ {
+ pszSymbol = sym.Name;
+
+ if (_SymUnDName(&sym, rgchUndec, COUNT_OF(rgchUndec)-1))
+ {
+ pszSymbol = rgchUndec;
+ }
+ }
+ else
+ {
+ pszSymbol = "<no symbol>";
+ }
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ pszSymbol = "<EX: no symbol>";
+ psi->dwOffset = dwAddr - mi.BaseOfImage;
+ }
+
+ strcpy_s(psi->achSymbol, _countof(psi->achSymbol), pszSymbol);
+}
+
+/****************************************************************************
+* FunctionTableAccess *
+*---------------------*
+* Description:
+* Helper for imagehlp's StackWalk API.
+****************************************************************************/
+LPVOID __stdcall FunctionTableAccess
+(
+HANDLE hProcess,
+DWORD_PTR dwPCAddr
+)
+{
+ WRAPPER_NO_CONTRACT;
+
+ HANDLE hFuncEntry = _SymFunctionTableAccess( hProcess, dwPCAddr );
+
+#if defined(HOST_64BIT)
+ if (hFuncEntry == NULL)
+ {
+ if (_GetRuntimeStackWalkInfo == NULL)
+ {
+ _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo)
+ GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo");
+ if (_GetRuntimeStackWalkInfo == NULL)
+ return NULL;
+ }
+
+ _GetRuntimeStackWalkInfo((ULONG64)dwPCAddr, NULL, (UINT_PTR*)(&hFuncEntry));
+ }
+#endif // HOST_64BIT
+
+ return hFuncEntry;
+}
+
+/****************************************************************************
+* GetModuleBase *
+*---------------*
+* Description:
+* Helper for imagehlp's StackWalk API. Retrieves the base address of
+* the module containing the giving virtual address.
+*
+* NOTE: If the module information for the given module hasnot yet been
+* loaded, then it is loaded on this call.
+*
+* Return:
+* Base virtual address where the module containing ReturnAddress is
+* loaded, or 0 if the address cannot be determined.
+****************************************************************************/
+DWORD_PTR __stdcall GetModuleBase
+(
+HANDLE hProcess,
+DWORD_PTR dwAddr
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ PLAT_IMAGEHLP_MODULE ModuleInfo;
+ ModuleInfo.SizeOfStruct = sizeof(ModuleInfo);
+
+ if (_SymGetModuleInfo(hProcess, dwAddr, &ModuleInfo))
+ {
+ return ModuleInfo.BaseOfImage;
+ }
+ else
+ {
+ MEMORY_BASIC_INFORMATION mbi;
+
+ if (VirtualQueryEx(hProcess, (LPVOID)dwAddr, &mbi, sizeof(mbi)))
+ {
+ if (mbi.Type & MEM_IMAGE)
+ {
+ char achFile[MAX_LONGPATH] = {0};
+ DWORD cch;
+
+ cch = GetModuleFileNameA(
+ (HINSTANCE)mbi.AllocationBase,
+ achFile,
+ MAX_LONGPATH);
+
+ // Ignore the return code since we can't do anything with it.
+ _SymLoadModule(
+ hProcess,
+ NULL,
+ ((cch) ? achFile : NULL),
+ NULL,
+ (DWORD_PTR)mbi.AllocationBase,
+ 0);
+
+ return (DWORD_PTR)mbi.AllocationBase;
+ }
+ }
+ }
+
+#if defined(HOST_64BIT)
+ if (_GetRuntimeStackWalkInfo == NULL)
+ {
+ _GetRuntimeStackWalkInfo = (pfn_GetRuntimeStackWalkInfo)
+ GetProcAddress(GetCLRModuleHack(), "GetRuntimeStackWalkInfo");
+ if (_GetRuntimeStackWalkInfo == NULL)
+ return NULL;
+ }
+
+ DWORD_PTR moduleBase;
+ _GetRuntimeStackWalkInfo((ULONG64)dwAddr, (UINT_PTR*)&moduleBase, NULL);
+ if (moduleBase != NULL)
+ return moduleBase;
+#endif // HOST_64BIT
+
+ return 0;
+}
+
+#if !defined(DACCESS_COMPILE)
+/****************************************************************************
+* GetStackBacktrace *
+*-------------------*
+* Description:
+* Gets a stacktrace of the current stack, including symbols.
+*
+* Return:
+* The number of elements actually retrieved.
+****************************************************************************/
+
+UINT GetStackBacktrace
+(
+UINT ifrStart, // How many stack elements to skip before starting.
+UINT cfrTotal, // How many elements to trace after starting.
+DWORD_PTR* pdwEip, // Array to be filled with stack addresses.
+SYM_INFO* psiSymbols, // This array is filled with symbol information.
+ // It should be big enough to hold cfrTotal elts.
+ // If NULL, no symbol information is stored.
+CONTEXT * pContext // Context to use (or NULL to use current)
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ UINT nElements = 0;
+ DWORD_PTR* pdw = pdwEip;
+ SYM_INFO* psi = psiSymbols;
+
+ MagicInit();
+
+ memset(pdwEip, 0, cfrTotal*sizeof(DWORD_PTR));
+
+ if (psiSymbols)
+ {
+ memset(psiSymbols, 0, cfrTotal * sizeof(SYM_INFO));
+ }
+
+ if (!g_fLoadedImageHlp)
+ {
+ return 0;
+ }
+
+ CONTEXT context;
+ if (pContext == NULL)
+ {
+ ClrCaptureContext(&context);
+ }
+ else
+ {
+ memcpy(&context, pContext, sizeof(CONTEXT));
+ }
+
+#ifdef HOST_64BIT
+ STACKFRAME64 stkfrm;
+ memset(&stkfrm, 0, sizeof(STACKFRAME64));
+#else
+ STACKFRAME stkfrm;
+ memset(&stkfrm, 0, sizeof(STACKFRAME));
+#endif
+
+ stkfrm.AddrPC.Mode = AddrModeFlat;
+ stkfrm.AddrStack.Mode = AddrModeFlat;
+ stkfrm.AddrFrame.Mode = AddrModeFlat;
+#if defined(_M_IX86)
+ stkfrm.AddrPC.Offset = context.Eip;
+ stkfrm.AddrStack.Offset = context.Esp;
+ stkfrm.AddrFrame.Offset = context.Ebp; // Frame Pointer
+#endif
+
+#ifndef HOST_X86
+ // If we don't have a user-supplied context, then don't skip any frames.
+ // So ignore this function (GetStackBackTrace)
+ // ClrCaptureContext on x86 gives us the ESP/EBP/EIP of its caller's caller
+ // so we don't need to do this.
+ if (pContext == NULL)
+ {
+ ifrStart += 1;
+ }
+#endif // !HOST_X86
+
+ for (UINT i = 0; i < ifrStart + cfrTotal; i++)
+ {
+ if (!_StackWalk(IMAGE_FILE_MACHINE_NATIVE,
+ g_hProcess,
+ GetCurrentThread(),
+ &stkfrm,
+ &context,
+ NULL,
+ (PFUNCTION_TABLE_ACCESS_ROUTINE)FunctionTableAccess,
+ (PGET_MODULE_BASE_ROUTINE)GetModuleBase,
+ NULL))
+ {
+ break;
+ }
+
+ if (i >= ifrStart)
+ {
+ *pdw++ = stkfrm.AddrPC.Offset;
+ nElements++;
+
+ if (psi)
+ {
+ FillSymbolInfo(psi++, stkfrm.AddrPC.Offset);
+ }
+ }
+ }
+
+ LOCAL_ASSERT(nElements == (UINT)(pdw - pdwEip));
+ return nElements;
+}
+#endif // !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromSymbolInfo *
+*-------------------------*
+* Description:
+* Actually prints the info into the string for the symbol.
+****************************************************************************/
+
+#ifdef HOST_64BIT
+ #define FMT_ADDR_BARE "%08x`%08x"
+ #define FMT_ADDR_OFFSET "%llX"
+#else
+ #define FMT_ADDR_BARE "%08x"
+ #define FMT_ADDR_OFFSET "%lX"
+#endif
+
+void GetStringFromSymbolInfo
+(
+DWORD_PTR dwAddr,
+SYM_INFO *psi, // @parm Pointer to SYMBOL_INFO. Can be NULL.
+__out_ecount (cchMaxAssertStackLevelStringLen) CHAR *pszString // @parm Place to put string.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ LOCAL_ASSERT(pszString);
+
+ // <module>! <symbol> + 0x<offset> 0x<addr>\n
+
+ if (psi)
+ {
+ sprintf_s(pszString,
+ cchMaxAssertStackLevelStringLen,
+ "%s! %s + 0x" FMT_ADDR_OFFSET " (0x" FMT_ADDR_BARE ")",
+ (psi->achModule[0]) ? psi->achModule : "<no module>",
+ (psi->achSymbol[0]) ? psi->achSymbol : "<no symbol>",
+ psi->dwOffset,
+ DBG_ADDR(dwAddr));
+ }
+ else
+ {
+ sprintf_s(pszString, cchMaxAssertStackLevelStringLen, "<symbols not available> (0x%p)", (void *)dwAddr);
+ }
+
+ LOCAL_ASSERT(strlen(pszString) < cchMaxAssertStackLevelStringLen);
+}
+
+#if !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromStackLevels *
+*--------------------------*
+* Description:
+* Retrieves a string from the stack frame. If more than one frame, they
+* are separated by newlines
+****************************************************************************/
+void GetStringFromStackLevels
+(
+UINT ifrStart, // @parm How many stack elements to skip before starting.
+UINT cfrTotal, // @parm How many elements to trace after starting.
+ // Can't be more than cfrMaxAssertStackLevels.
+__out_ecount(cchMaxAssertStackLevelStringLen * cfrTotal) CHAR *pszString, // @parm Place to put string.
+ // Max size will be cchMaxAssertStackLevelStringLen * cfrTotal.
+CONTEXT * pContext // @parm Context to start the stack trace at; null for current context.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ LOCAL_ASSERT(pszString);
+ LOCAL_ASSERT(cfrTotal < cfrMaxAssertStackLevels);
+
+ *pszString = '\0';
+
+ if (cfrTotal == 0)
+ {
+ return;
+ }
+
+ DWORD_PTR rgdwStackAddrs[cfrMaxAssertStackLevels];
+ SYM_INFO rgsi[cfrMaxAssertStackLevels];
+
+ // Ignore this function (GetStringFromStackLevels) if we don't have a user-supplied context.
+ if (pContext == NULL)
+ {
+ ifrStart += 1;
+ }
+
+ UINT uiRetrieved =
+ GetStackBacktrace(ifrStart, cfrTotal, rgdwStackAddrs, rgsi, pContext);
+
+ // First level
+ CHAR aszLevel[cchMaxAssertStackLevelStringLen];
+ GetStringFromSymbolInfo(rgdwStackAddrs[0], &rgsi[0], aszLevel);
+
+ size_t bufSize = cchMaxAssertStackLevelStringLen * cfrTotal;
+
+ strcpy_s(pszString, bufSize, aszLevel);
+
+ // Additional levels
+ for (UINT i = 1; i < uiRetrieved; ++i)
+ {
+ strcat_s(pszString, bufSize, "\n");
+ GetStringFromSymbolInfo(rgdwStackAddrs[i],
+ &rgsi[i], aszLevel);
+ strcat_s(pszString, bufSize, aszLevel);
+ }
+
+ LOCAL_ASSERT(strlen(pszString) <= cchMaxAssertStackLevelStringLen * cfrTotal);
+}
+#endif // !defined(DACCESS_COMPILE)
+
+/****************************************************************************
+* GetStringFromAddr *
+*-------------------*
+* Description:
+* Returns a string from an address.
+****************************************************************************/
+void GetStringFromAddr
+(
+DWORD_PTR dwAddr,
+__out_ecount(cchMaxAssertStackLevelStringLen) LPSTR szString // Place to put string.
+ // Buffer must hold at least cchMaxAssertStackLevelStringLen.
+)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ LOCAL_ASSERT(szString);
+
+ SYM_INFO si;
+ FillSymbolInfo(&si, dwAddr);
+
+ sprintf_s(szString,
+ cchMaxAssertStackLevelStringLen,
+ "%s! %s + 0x%p (0x%p)",
+ (si.achModule[0]) ? si.achModule : "<no module>",
+ (si.achSymbol[0]) ? si.achSymbol : "<no symbol>",
+ (void*)si.dwOffset,
+ (void*)dwAddr);
+}
+
+/****************************************************************************
+* MagicDeinit *
+*-------------*
+* Description:
+* Cleans up for the symbol loading code. Should be called before exit
+* to free the dynamically loaded imagehlp.dll.
+****************************************************************************/
+void MagicDeinit(void)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+
+ if (g_hinstImageHlp)
+ {
+ FreeLibrary(g_hinstImageHlp);
+
+ g_hinstImageHlp = NULL;
+ g_fLoadedImageHlp = FALSE;
+ }
+}
+
+#if defined(HOST_X86)
+/****************************************************************************
+* ClrCaptureContext *
+*-------------------*
+* Description:
+* Exactly the contents of RtlCaptureContext for Win7 - Win2K doesn't
+* support this, so we need it for CoreCLR 4, if we require Win2K support
+****************************************************************************/
+extern "C" __declspec(naked) void __stdcall
+ClrCaptureContext(__out PCONTEXT ctx)
+{
+ __asm {
+ push ebx;
+ mov ebx,dword ptr [esp+8]
+ mov dword ptr [ebx+0B0h],eax
+ mov dword ptr [ebx+0ACh],ecx
+ mov dword ptr [ebx+0A8h],edx
+ mov eax,dword ptr [esp]
+ mov dword ptr [ebx+0A4h],eax
+ mov dword ptr [ebx+0A0h],esi
+ mov dword ptr [ebx+09Ch],edi
+ mov word ptr [ebx+0BCh],cs
+ mov word ptr [ebx+098h],ds
+ mov word ptr [ebx+094h],es
+ mov word ptr [ebx+090h],fs
+ mov word ptr [ebx+08Ch],gs
+ mov word ptr [ebx+0C8h],ss
+ pushfd
+ pop dword ptr [ebx+0C0h]
+ mov eax,dword ptr [ebp+4]
+ mov dword ptr [ebx+0B8h],eax
+ mov eax,dword ptr [ebp]
+ mov dword ptr [ebx+0B4h],eax
+ lea eax,[ebp+8]
+ mov dword ptr [ebx+0C4h],eax
+ mov dword ptr [ebx],10007h
+ pop ebx
+ ret 4
+ }
+}
+#endif // HOST_X86
diff --git a/src/coreclr/utilcode/stdafx.h b/src/coreclr/utilcode/stdafx.h
new file mode 100644
index 00000000000..f04d0e47f8e
--- /dev/null
+++ b/src/coreclr/utilcode/stdafx.h
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// stdafx.h
+//
+
+//
+// Common include file for utility code.
+//*****************************************************************************
+#pragma once
+
+#include <switches.h>
+#include <crtwrap.h>
+
+#define IN_WINFIX_CPP
+
+#include <winwrap.h>
+
+#include "volatile.h"
+#include "static_assert.h"
+
diff --git a/src/coreclr/utilcode/stgpool.cpp b/src/coreclr/utilcode/stgpool.cpp
new file mode 100644
index 00000000000..7c4c203e754
--- /dev/null
+++ b/src/coreclr/utilcode/stgpool.cpp
@@ -0,0 +1,2423 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// StgPool.cpp
+//
+
+//
+// Pools are used to reduce the amount of data actually required in the database.
+// This allows for duplicate string and binary values to be folded into one
+// copy shared by the rest of the database. Strings are tracked in a hash
+// table when insert/changing data to find duplicates quickly. The strings
+// are then persisted consecutively in a stream in the database format.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+#include <posterror.h> // Error handling.
+#include <safemath.h> // CLRSafeInt integer overflow checking
+#include "../md/inc/streamutil.h"
+
+#include "ex.h"
+
+#ifdef FEATURE_PREJIT
+#include <corcompile.h>
+#endif
+
+using namespace StreamUtil;
+
+#define MAX_CHAIN_LENGTH 20 // Max chain length before rehashing.
+
+//
+//
+// StgPool
+//
+//
+
+
+//*****************************************************************************
+// Free any memory we allocated.
+//*****************************************************************************
+StgPool::~StgPool()
+{
+ WRAPPER_NO_CONTRACT;
+
+ Uninit();
+} // StgPool::~StgPool()
+
+
+//*****************************************************************************
+// Init the pool for use. This is called for both the create empty case.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+ _ASSERTE(m_pNextSeg == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_cbCurSegOffset == 0);
+ _ASSERTE(m_cbSegSize == 0);
+ _ASSERTE(m_cbSegNext == 0);
+
+ m_bReadOnly = false;
+ m_bFree = false;
+
+ return S_OK;
+} // StgPool::InitNew
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+ _ASSERTE(m_pNextSeg == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_cbCurSegOffset == 0);
+
+ // Create case requires no further action.
+ if (!pData)
+ return (E_INVALIDARG);
+
+ // Might we be extending this heap?
+ m_bReadOnly = bReadOnly;
+
+
+ m_pSegData = reinterpret_cast<BYTE*>(pData);
+ m_cbSegSize = iSize;
+ m_cbSegNext = iSize;
+
+ m_bFree = false;
+
+ return (S_OK);
+} // StgPool::InitOnMem
+
+//*****************************************************************************
+// Called when the pool must stop accessing memory passed to InitOnMem().
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::TakeOwnershipOfInitMem()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // If the pool doesn't have a pointer to non-owned memory, done.
+ if (m_bFree)
+ return (S_OK);
+
+ // If the pool doesn't have a pointer to memory at all, done.
+ if (m_pSegData == m_zeros)
+ {
+ _ASSERTE(m_cbSegSize == 0);
+ return (S_OK);
+ }
+
+ // Get some memory to keep.
+ BYTE *pData = new (nothrow) BYTE[m_cbSegSize+4];
+ if (pData == 0)
+ return (PostError(OutOfMemory()));
+
+ // Copy the old data to the new memory.
+ memcpy(pData, m_pSegData, m_cbSegSize);
+ m_pSegData = pData;
+ m_bFree = true;
+
+ return (S_OK);
+} // StgPool::TakeOwnershipOfInitMem
+
+//*****************************************************************************
+// Clear out this pool. Cannot use until you call InitNew.
+//*****************************************************************************
+void StgPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Free base segment, if appropriate.
+ if (m_bFree && (m_pSegData != m_zeros))
+ {
+ delete [] m_pSegData;
+ m_bFree = false;
+ }
+
+ // Free chain, if any.
+ StgPoolSeg *pSeg = m_pNextSeg;
+ while (pSeg)
+ {
+ StgPoolSeg *pNext = pSeg->m_pNextSeg;
+ delete [] (BYTE*)pSeg;
+ pSeg = pNext;
+ }
+
+ // Clear vars.
+ m_pSegData = (BYTE*)m_zeros;
+ m_cbSegSize = m_cbSegNext = 0;
+ m_pNextSeg = 0;
+ m_pCurSeg = this;
+ m_cbCurSegOffset = 0;
+} // StgPool::Uninit
+
+//*****************************************************************************
+// Called to copy the pool to writable memory, reset the r/o bit.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::ConvertToRW()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr; // A result.
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ IfFailRet(SetHash(true));
+
+ m_bReadOnly = false;
+
+ return S_OK;
+} // StgPool::ConvertToRW
+
+//*****************************************************************************
+// Turn hashing off or on. Real implementation as required in subclass.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ return S_OK;
+} // StgPool::SetHash
+
+//*****************************************************************************
+// Trim any empty final segment.
+//*****************************************************************************
+void StgPool::Trim()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // If no chained segments, nothing to do.
+ if (m_pNextSeg == 0)
+ return;
+
+ // Handle special case for a segment that was completely unused.
+ if (m_pCurSeg->m_cbSegNext == 0)
+ {
+ // Find the segment which points to the empty segment.
+ StgPoolSeg *pPrev;
+ for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
+ _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
+
+ // Free the empty segment.
+ delete [] (BYTE*) m_pCurSeg;
+
+ // Fix the pCurSeg pointer.
+ pPrev->m_pNextSeg = 0;
+ m_pCurSeg = pPrev;
+
+ // Adjust the base offset, because the PREVIOUS seg is now current.
+ _ASSERTE(m_pCurSeg->m_cbSegNext <= m_cbCurSegOffset);
+ m_cbCurSegOffset = m_cbCurSegOffset - m_pCurSeg->m_cbSegNext;
+ }
+} // StgPool::Trim
+
+//*****************************************************************************
+// Allocate memory if we don't have any, or grow what we have. If successful,
+// then at least iRequired bytes will be allocated.
+//*****************************************************************************
+bool StgPool::Grow( // true if successful.
+ ULONG iRequired) // Min required bytes to allocate.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return FALSE;);
+ }
+ CONTRACTL_END
+
+ ULONG iNewSize; // New size we want.
+ StgPoolSeg *pNew; // Temp pointer for malloc.
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Would this put the pool over 2GB?
+ if ((m_cbCurSegOffset + iRequired) > INT_MAX)
+ return (false);
+
+ // Adjust grow size as a ratio to avoid too many reallocs.
+ if ((m_pCurSeg->m_cbSegNext + m_cbCurSegOffset) / m_ulGrowInc >= 3)
+ m_ulGrowInc *= 2;
+
+ // NOTE: MD\DataSource\RemoteMDInternalRWSource has taken a dependency that there
+ // won't be more than 1000 segments. Given the current exponential growth algorithm
+ // we'll never get anywhere close to that, but if the algorithm changes to allow for
+ // many segments, please update that source as well.
+
+ // If first time, handle specially.
+ if (m_pSegData == m_zeros)
+ {
+ // Allocate the buffer.
+ iNewSize = max(m_ulGrowInc, iRequired);
+ BYTE *pSegData = new (nothrow) BYTE[iNewSize + 4];
+ if (pSegData == NULL)
+ return false;
+ m_pSegData = pSegData;
+
+ // Will need to delete it.
+ m_bFree = true;
+
+ // How big is this initial segment?
+ m_cbSegSize = iNewSize;
+
+ // Do some validation of var fields.
+ _ASSERTE(m_cbSegNext == 0);
+ _ASSERTE(m_pCurSeg == this);
+ _ASSERTE(m_pNextSeg == NULL);
+
+ return true;
+ }
+
+ // Allocate the new space enough for header + data.
+ iNewSize = (ULONG)(max(m_ulGrowInc, iRequired) + sizeof(StgPoolSeg));
+ pNew = (StgPoolSeg *)new (nothrow) BYTE[iNewSize+4];
+ if (pNew == NULL)
+ return false;
+
+ // Set the fields in the new segment.
+ pNew->m_pSegData = reinterpret_cast<BYTE*>(pNew) + sizeof(StgPoolSeg);
+ _ASSERTE(ALIGN4BYTE(reinterpret_cast<ULONG_PTR>(pNew->m_pSegData)) == reinterpret_cast<ULONG_PTR>(pNew->m_pSegData));
+ pNew->m_pNextSeg = 0;
+ pNew->m_cbSegSize = iNewSize - sizeof(StgPoolSeg);
+ pNew->m_cbSegNext = 0;
+
+ // Calculate the base offset of the new segment.
+ m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
+
+ // Handle special case for a segment that was completely unused.
+ //<TODO>@todo: Trim();</TODO>
+ if (m_pCurSeg->m_cbSegNext == 0)
+ {
+ // Find the segment which points to the empty segment.
+ StgPoolSeg *pPrev;
+ for (pPrev = this; pPrev && pPrev->m_pNextSeg != m_pCurSeg; pPrev = pPrev->m_pNextSeg);
+ _ASSERTE(pPrev && pPrev->m_pNextSeg == m_pCurSeg);
+
+ // Free the empty segment.
+ delete [] (BYTE *) m_pCurSeg;
+
+ // Link in the new segment.
+ pPrev->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return true;
+ }
+
+ // Fix the size of the old segment.
+ m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
+
+ // Link the new segment into the chain.
+ m_pCurSeg->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return true;
+} // StgPool::Grow
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::AddSegment(
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy) // If true, make a copy of the data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ StgPoolSeg *pNew; // Temp pointer for malloc.
+
+
+ // If we need to copy the data, just grow the heap by enough to take the
+ // the new data, and copy it in.
+ if (bCopy)
+ {
+ void *pDataToAdd = new (nothrow) BYTE[cbData];
+ IfNullRet(pDataToAdd);
+ memcpy(pDataToAdd, pData, cbData);
+ pData = pDataToAdd;
+ }
+
+ // If first time, handle specially.
+ if (m_pSegData == m_zeros)
+ { // Data was passed in.
+ m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
+ m_cbSegSize = cbData;
+ m_cbSegNext = cbData;
+ _ASSERTE(m_pNextSeg == NULL);
+
+ // Will not delete it.
+ m_bFree = false;
+
+ return S_OK;
+ }
+
+ // Not first time. Handle a completely empty tail segment.
+ Trim();
+
+ // Abandon any space past the end of the current live data.
+ _ASSERTE(m_pCurSeg->m_cbSegSize >= m_pCurSeg->m_cbSegNext);
+ m_pCurSeg->m_cbSegSize = m_pCurSeg->m_cbSegNext;
+
+ // Allocate a new segment header.
+ pNew = (StgPoolSeg *) new (nothrow) BYTE[sizeof(StgPoolSeg)];
+ IfNullRet(pNew);
+
+ // Set the fields in the new segment.
+ pNew->m_pSegData = reinterpret_cast<BYTE*>(const_cast<void*>(pData));
+ pNew->m_pNextSeg = NULL;
+ pNew->m_cbSegSize = cbData;
+ pNew->m_cbSegNext = cbData;
+
+ // Calculate the base offset of the new segment.
+ m_cbCurSegOffset = m_cbCurSegOffset + m_pCurSeg->m_cbSegNext;
+
+ // Link the segment into the chain.
+ _ASSERTE(m_pCurSeg->m_pNextSeg == NULL);
+ m_pCurSeg->m_pNextSeg = pNew;
+ m_pCurSeg = pNew;
+
+ return S_OK;
+} // StgPool::AddSegment
+
+#ifndef DACCESS_COMPILE
+//*****************************************************************************
+// The entire string pool is written to the given stream. The stream is aligned
+// to a 4 byte boundary.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::PersistToStream(
+ IStream *pIStream) // The stream to write to.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ ULONG cbTotal; // Total bytes written.
+ StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ cbTotal = 0;
+
+ EX_TRY
+ {
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // . . . write and count the data.
+ if (FAILED(hr = pIStream->Write(pSeg->m_pSegData, pSeg->m_cbSegNext, 0)))
+ break;
+ cbTotal += pSeg->m_cbSegNext;
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ // Align to variable (0-4 byte) boundary.
+ UINT32 cbTotalAligned;
+ if (FAILED(Align(cbTotal, &cbTotalAligned)))
+ {
+ hr = COR_E_BADIMAGEFORMAT;
+ }
+ else
+ {
+ if (cbTotalAligned > cbTotal)
+ {
+ _ASSERTE(sizeof(hr) >= 3);
+ hr = 0;
+ hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
+ }
+ }
+ }
+ }
+ EX_CATCH
+ {
+ hr = E_FAIL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return hr;
+} // StgPool::PersistToStream
+#endif //!DACCESS_COMPILE
+
+//*****************************************************************************
+// The entire string pool is written to the given stream. The stream is aligned
+// to a 4 byte boundary.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::PersistPartialToStream(
+ IStream *pIStream, // The stream to write to.
+ ULONG iOffset) // Starting offset.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK; // A result.
+ ULONG cbTotal; // Total bytes written.
+ StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ cbTotal = 0;
+
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // If this data should be skipped...
+ if (iOffset >= pSeg->m_cbSegNext)
+ { // Skip it
+ iOffset -= pSeg->m_cbSegNext;
+ }
+ else
+ { // At least some data should be written, so write and count the data.
+ IfFailRet(pIStream->Write(pSeg->m_pSegData+iOffset, pSeg->m_cbSegNext-iOffset, 0));
+ cbTotal += pSeg->m_cbSegNext-iOffset;
+ iOffset = 0;
+ }
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ // Align to variable (0-4 byte) boundary.
+ UINT32 cbTotalAligned;
+ if (FAILED(Align(cbTotal, &cbTotalAligned)))
+ {
+ return COR_E_BADIMAGEFORMAT;
+ }
+ if (cbTotalAligned > cbTotal)
+ {
+ _ASSERTE(sizeof(hr) >= 3);
+ hr = 0;
+ hr = pIStream->Write(&hr, cbTotalAligned - cbTotal, 0);
+ }
+
+ return hr;
+} // StgPool::PersistPartialToStream
+
+// Copies data from pSourcePool starting at index nStartSourceIndex.
+__checkReturn
+HRESULT
+StgPool::CopyPool(
+ UINT32 nStartSourceIndex,
+ const StgPool *pSourcePool)
+{
+ HRESULT hr;
+ UINT32 cbDataSize;
+ BYTE *pbData = NULL;
+
+ if (nStartSourceIndex == pSourcePool->GetRawSize())
+ { // There's nothing to copy
+ return S_OK;
+ }
+ if (nStartSourceIndex > pSourcePool->GetRawSize())
+ { // Invalid input
+ Debug_ReportInternalError("The caller should not pass invalid start index in the pool.");
+ IfFailGo(METADATA_E_INDEX_NOTFOUND);
+ }
+
+ // Allocate new segment
+ cbDataSize = pSourcePool->GetRawSize() - nStartSourceIndex;
+ pbData = new (nothrow) BYTE[cbDataSize];
+ IfNullGo(pbData);
+
+ // Copy data to the new segment
+ UINT32 cbCopiedDataSize;
+ IfFailGo(pSourcePool->CopyData(
+ nStartSourceIndex,
+ pbData,
+ cbDataSize,
+ &cbCopiedDataSize));
+ // Check that we copied everything
+ if (cbDataSize != cbCopiedDataSize)
+ {
+ Debug_ReportInternalError("It is expected to copy everything from the source pool.");
+ IfFailGo(E_FAIL);
+ }
+
+ // Add the newly allocated segment to the pool
+ IfFailGo(AddSegment(
+ pbData,
+ cbDataSize,
+ false)); // fCopyData
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pbData != NULL)
+ {
+ delete [] pbData;
+ }
+ }
+ return hr;
+} // StgPool::CopyPool
+
+// Copies data from the pool into a buffer. It will correctly walk all segments for the copy.
+__checkReturn
+HRESULT
+StgPool::CopyData(
+ UINT32 nOffset,
+ BYTE *pBuffer,
+ UINT32 cbBuffer,
+ UINT32 *pcbWritten) const
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ PRECONDITION(CheckPointer(pBuffer));
+ PRECONDITION(CheckPointer(pcbWritten));
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+ const StgPoolSeg *pSeg; // A segment being written.
+
+ _ASSERTE(m_pSegData != m_zeros);
+
+ // Start with the base segment.
+ pSeg = this;
+ *pcbWritten = 0;
+
+ // As long as there is data, write it.
+ while (pSeg != NULL)
+ {
+ // If there is data in the segment . . .
+ if (pSeg->m_cbSegNext)
+ { // If this data should be skipped...
+ if (nOffset >= pSeg->m_cbSegNext)
+ { // Skip it
+ nOffset -= pSeg->m_cbSegNext;
+ }
+ else
+ {
+ ULONG nNumBytesToCopy = pSeg->m_cbSegNext - nOffset;
+ if (nNumBytesToCopy > (cbBuffer - *pcbWritten))
+ {
+ _ASSERTE(!"Buffer isn't big enough to copy everything!");
+ nNumBytesToCopy = cbBuffer - *pcbWritten;
+ }
+
+ memcpy(pBuffer + *pcbWritten, pSeg->m_pSegData+nOffset, nNumBytesToCopy);
+
+ *pcbWritten += nNumBytesToCopy;
+ nOffset = 0;
+ }
+ }
+
+ // Get the next segment.
+ pSeg = pSeg->m_pNextSeg;
+ }
+
+ return hr;
+} // StgPool::CopyData
+
+//*****************************************************************************
+// Get a pointer to the data at some offset. May require traversing the
+// chain of extensions. It is the caller's responsibility not to attempt
+// to access data beyond the end of a segment.
+// This is an internal accessor, and should only be called when the data
+// is not in the base segment.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgPool::GetData_i(
+ UINT32 nOffset,
+ MetaData::DataBlob *pData)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Shouldn't be called on base segment.
+ _ASSERTE(nOffset >= m_cbSegNext);
+ StgPoolSeg *pSeg = this;
+
+ while ((nOffset > 0) && (nOffset >= pSeg->m_cbSegNext))
+ {
+ // On to next segment.
+ nOffset -= pSeg->m_cbSegNext;
+ pSeg = pSeg->m_pNextSeg;
+
+ // Is there a next?
+ if (pSeg == NULL)
+ {
+ Debug_ReportError("Invalid offset passed - reached end of pool.");
+ pData->Clear();
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+ }
+
+ // For the case where we want to read the first item and the pool is empty.
+ if (nOffset == pSeg->m_cbSegNext)
+ { // Can only be if both == 0
+ Debug_ReportError("Invalid offset passed - it is at the end of pool.");
+ pData->Clear();
+ return CLDB_E_INDEX_NOTFOUND;
+ }
+
+ pData->Init(pSeg->m_pSegData + nOffset, pSeg->m_cbSegNext - nOffset);
+
+ return S_OK;
+} // StgPool::GetData_i
+
+//
+//
+// StgStringPool
+//
+//
+
+
+//*****************************************************************************
+// Create a new, empty string pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+ UINT32 nEmptyStringOffset;
+
+ // Let base class intialize.
+ IfFailRet(StgPool::InitNew());
+
+ // Set initial table sizes, if specified.
+ if (cbSize > 0)
+ {
+ if (!Grow(cbSize))
+ {
+ return E_OUTOFMEMORY;
+ }
+ }
+ if (cItems > 0)
+ {
+ m_Hash.SetBuckets(cItems);
+ }
+
+ // Init with empty string.
+ IfFailRet(AddString("", &nEmptyStringOffset));
+ // Empty string had better be at offset 0.
+ _ASSERTE(nEmptyStringOffset == 0);
+
+ return hr;
+} // StgStringPool::InitNew
+
+//*****************************************************************************
+// Load a string heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new strings.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // There may be up to three extra '\0' characters appended for padding. Trim them.
+ char *pchData = reinterpret_cast<char*>(pData);
+ while (iSize > 1 && pchData[iSize-1] == 0 && pchData[iSize-2] == 0)
+ --iSize;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ if (!bReadOnly)
+ {
+ IfFailRet(TakeOwnershipOfInitMem());
+ IfFailRet(RehashStrings());
+ }
+
+ return hr;
+} // StgStringPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgStringPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgStringPool::Uninit
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all strings.
+ if (bHash)
+ hr = RehashStrings();
+
+ m_bHash = bHash;
+ return (hr);
+} // StgStringPool::SetHash
+
+//*****************************************************************************
+// The string will be added to the pool. The offset of the string in the pool
+// is returned in *piOffset. If the string is already in the pool, then the
+// offset will be to the existing copy of the string.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::AddString(
+ LPCSTR szString, // The string to add to pool.
+ UINT32 *pnOffset) // Return offset of string here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ STRINGHASH *pHash; // Hash item for add.
+ ULONG iLen; // To handle non-null strings.
+ LPSTR pData; // Pointer to location for new string.
+ HRESULT hr;
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Null pointer is an error.
+ if (szString == 0)
+ return (PostError(E_INVALIDARG));
+
+ // Find the real length we need in buffer.
+ iLen = (ULONG)(strlen(szString) + 1);
+
+ // Where to put the new string?
+ if (iLen > GetCbSegAvailable())
+ {
+ if (!Grow(iLen))
+ return (PostError(OutOfMemory()));
+ }
+ pData = reinterpret_cast<LPSTR>(GetNextLocation());
+
+ // Copy the data into the buffer.
+ strcpy_s(pData, iLen, szString);
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(pData, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new string.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ *pnOffset = pHash->iOffset = GetNextOffset();
+ SegAllocate(iLen);
+
+ // Check for hash chains that are too long.
+ if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
+ {
+ IfFailRet(RehashStrings());
+ }
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+ }
+ // Probably an import which defers the hash table for speed.
+ else
+ {
+ *pnOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ return S_OK;
+} // StgStringPool::AddString
+
+//*****************************************************************************
+// Add a string to the pool with Unicode to UTF8 conversion.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::AddStringW(
+ LPCWSTR szString, // The string to add to pool.
+ UINT32 *pnOffset) // Return offset of string here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ STRINGHASH *pHash; // Hash item for add.
+ ULONG iLen; // Correct length after conversion.
+ LPSTR pData; // Pointer to location for new string.
+
+ _ASSERTE(!m_bReadOnly);
+
+ // Null pointer is an error.
+ if (szString == 0)
+ return (PostError(E_INVALIDARG));
+
+ // Special case empty string.
+ if (*szString == '\0')
+ {
+ *pnOffset = 0;
+ return (S_OK);
+ }
+
+ // How many bytes will be required in the heap?
+ iLen = ::WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ szString,
+ -1, // null-terminated string
+ NULL,
+ 0,
+ NULL,
+ NULL);
+ // WCTMB includes trailing 0 if (when passing parameter #4 (length) -1.
+
+ // Check for room.
+ if (iLen > GetCbSegAvailable())
+ {
+ if (!Grow(iLen))
+ return (PostError(OutOfMemory()));
+ }
+ pData = reinterpret_cast<LPSTR>(GetNextLocation());
+
+ // Convert the data in place to the correct location.
+ iLen = ::WszWideCharToMultiByte(
+ CP_UTF8,
+ 0,
+ szString,
+ -1,
+ pData,
+ GetCbSegAvailable(),
+ NULL,
+ NULL);
+ if (iLen == 0)
+ return (BadError(HRESULT_FROM_NT(GetLastError())));
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(pData, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new string.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ *pnOffset = pHash->iOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+ }
+ // Probably an import which defers the hash table for speed.
+ else
+ {
+ *pnOffset = GetNextOffset();
+ SegAllocate(iLen);
+ }
+ return (S_OK);
+} // StgStringPool::AddStringW
+
+
+//*****************************************************************************
+// Clears out the existing hash table used to eliminate duplicates. Then
+// rebuilds the hash table from scratch based on the current data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgStringPool::RehashStrings()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ULONG iOffset; // Loop control.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ STRINGHASH *pHash; // Hash item for add.
+ LPCSTR pString; // A string;
+ ULONG iLen; // The string's length.
+ int iBuckets; // Buckets in the hash.
+ int iCount; // Items in the hash.
+ int iNewBuckets; // New count of buckets in the hash.
+
+ // Determine the new bucket size.
+ iBuckets = m_Hash.Buckets();
+ iCount = m_Hash.Count();
+ iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
+
+ // Remove any stale data.
+ m_Hash.Clear();
+ m_Hash.SetBuckets(iNewBuckets);
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each string, skipping initial empty string.
+ for (iSeg=iOffset=1; iOffset < iMax; )
+ {
+ // Get the string from the pool.
+ pString = reinterpret_cast<LPCSTR>(pSeg->m_pSegData + iSeg);
+ // Add the string to the hash table.
+ if ((pHash = m_Hash.Add(pString)) == 0)
+ return (PostError(OutOfMemory()));
+ pHash->iOffset = iOffset;
+
+ // Move to next string.
+ iLen = (ULONG)(strlen(pString) + 1);
+ iOffset += iLen;
+ iSeg += iLen;
+ if (iSeg >= pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgStringPool::RehashStrings
+
+//
+//
+// StgGuidPool
+//
+//
+
+__checkReturn
+HRESULT
+StgGuidPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems) // Estimated item count.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr; // A result.
+
+ if (FAILED(hr = StgPool::InitNew()))
+ return (hr);
+
+ // Set initial table sizes, if specified.
+ if (cbSize)
+ if (!Grow(cbSize))
+ return E_OUTOFMEMORY;
+ if (cItems)
+ m_Hash.SetBuckets(cItems);
+
+ return (S_OK);
+} // StgGuidPool::InitNew
+
+//*****************************************************************************
+// Load a Guid heap from persisted memory. If a copy of the data is made
+// (so that it may be updated), then a new hash table is generated which can
+// be used to elminate duplicates with new Guids.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::InitOnMem(
+ void *pData, // Predefined data.
+ ULONG iSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pData, iSize, bReadOnly));
+
+ // For init on existing mem case.
+ if (pData && iSize)
+ {
+ // If we cannot update, then we don't need a hash table.
+ if (bReadOnly)
+ return S_OK;
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ // Build the hash table on the data.
+ if (FAILED(hr = RehashGuids()))
+ {
+ Uninit();
+ return hr;
+ }
+ }
+
+ return S_OK;
+} // StgGuidPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgGuidPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgGuidPool::Uninit
+
+//*****************************************************************************
+// Add a segment to the chain of segments.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::AddSegment(
+ const void *pData, // The data.
+ ULONG cbData, // Size of the data.
+ bool bCopy) // If true, make a copy of the data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Want an integeral number of GUIDs.
+ _ASSERTE((cbData % sizeof(GUID)) == 0);
+
+ return StgPool::AddSegment(pData, cbData, bCopy);
+
+} // StgGuidPool::AddSegment
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all guids.
+ if (bHash)
+ hr = RehashGuids();
+
+ m_bHash = bHash;
+ return (hr);
+} // StgGuidPool::SetHash
+
+//*****************************************************************************
+// The Guid will be added to the pool. The index of the Guid in the pool
+// is returned in *piIndex. If the Guid is already in the pool, then the
+// index will be to the existing copy of the Guid.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::AddGuid(
+ const GUID *pGuid, // The Guid to add to pool.
+ UINT32 *pnIndex) // Return 1-based index of Guid here.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ GUIDHASH *pHash = NULL; // Hash item for add.
+
+ GUID guid = *pGuid;
+ SwapGuid(&guid);
+
+ // Special case for GUID_NULL
+ if (guid == GUID_NULL)
+ {
+ *pnIndex = 0;
+ return S_OK;
+ }
+
+ // If the hash table is to be kept built (default).
+ if (m_bHash)
+ {
+ // Find or add the entry.
+ pHash = m_Hash.Find(&guid, true);
+ if (!pHash)
+ return (PostError(OutOfMemory()));
+
+ // If the guid was found, just use it.
+ if (pHash->iIndex != 0xffffffff)
+ { // Return 1-based index.
+ *pnIndex = pHash->iIndex;
+ return S_OK;
+ }
+ }
+
+ // Space on heap for new guid?
+ if (sizeof(GUID) > GetCbSegAvailable())
+ {
+ if (!Grow(sizeof(GUID)))
+ return (PostError(OutOfMemory()));
+ }
+
+ // Copy the guid to the heap.
+ *reinterpret_cast<GUID*>(GetNextLocation()) = guid;
+
+ // Give the 1-based index back to caller.
+ *pnIndex = (GetNextOffset() / sizeof(GUID)) + 1;
+
+ // If hashing, save the 1-based index in the hash.
+ if (m_bHash)
+ pHash->iIndex = *pnIndex;
+
+ // Update heap counters.
+ SegAllocate(sizeof(GUID));
+
+ return S_OK;
+} // StgGuidPool::AddGuid
+
+//*****************************************************************************
+// Recompute the hashes for the pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgGuidPool::RehashGuids()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ ULONG iOffset; // Loop control.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ GUIDHASH *pHash; // Hash item for add.
+ GUID *pGuid; // A guid;
+
+ // Remove any stale data.
+ m_Hash.Clear();
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each guid.
+ for (iSeg=iOffset=0; iOffset < iMax; )
+ {
+ // Get a pointer to the guid.
+ pGuid = reinterpret_cast<GUID*>(pSeg->m_pSegData + iSeg);
+ // Add the guid to the hash table.
+ if ((pHash = m_Hash.Add(pGuid)) == 0)
+ return (PostError(OutOfMemory()));
+ pHash->iIndex = iOffset / sizeof(GUID);
+
+ // Move to next Guid.
+ iOffset += sizeof(GUID);
+ iSeg += sizeof(GUID);
+ if (iSeg > pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgGuidPool::RehashGuids
+
+//
+//
+// StgBlobPool
+//
+//
+
+
+
+//*****************************************************************************
+// Create a new, empty blob pool.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::InitNew(
+ ULONG cbSize, // Estimated size.
+ ULONG cItems, // Estimated item count.
+ BOOL fAddEmptryItem) // Should we add an empty item at offset 0
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class intialize.
+ IfFailRet(StgPool::InitNew());
+
+ // Set initial table sizes, if specified.
+ if (cbSize > 0)
+ {
+ if (!Grow(cbSize))
+ return E_OUTOFMEMORY;
+ }
+ if (cItems > 0)
+ m_Hash.SetBuckets(cItems);
+
+ // Init with empty blob.
+
+ // Normally must do this, regardless if we currently have anything in the pool.
+ // If we don't do this, the first blob that gets added to the pool will
+ // have an offset of 0. This will cause this blob to have a token of
+ // 0x70000000, which is considered a nil string token.
+ //
+ // By inserting a zero length blob into the pool the being with, we're
+ // assured that the first blob added to the pool will have an offset
+ // of 1 and a token of 0x70000001, which is a valid token.
+ //
+ // The only time we wouldn't want to do this is if we're reading in a delta metadata.
+ // Then, we don't care if the first string is at offset 0... when the delta gets applied,
+ // the string will get moved to the appropriate offset.
+ if (fAddEmptryItem)
+ {
+ MetaData::DataBlob emptyBlob(NULL, 0);
+ UINT32 nIndex_Ignore;
+ IfFailRet(AddBlob(&emptyBlob, &nIndex_Ignore));
+ // Empty blob better be at offset 0.
+ _ASSERTE(nIndex_Ignore == 0);
+ }
+ return hr;
+} // StgBlobPool::InitNew
+
+//*****************************************************************************
+// Init the blob pool for use. This is called for both create and read case.
+// If there is existing data and bCopyData is true, then the data is rehashed
+// to eliminate dupes in future adds.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::InitOnMem(
+ void *pBuf, // Predefined data.
+ ULONG iBufSize, // Size of data.
+ int bReadOnly) // true if append is forbidden.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr;
+
+ // Let base class init our memory structure.
+ IfFailRet(StgPool::InitOnMem(pBuf, iBufSize, bReadOnly));
+
+ // Init hash table from existing data.
+ // If we cannot update, we don't need a hash table.
+ if (bReadOnly)
+ {
+ return S_OK;
+ }
+
+ //<TODO>@todo: defer this until we hand out a pointer.</TODO>
+ IfFailRet(TakeOwnershipOfInitMem());
+
+ UINT32 nMaxOffset = GetNextOffset();
+ for (UINT32 nOffset = 0; nOffset < nMaxOffset; )
+ {
+ MetaData::DataBlob blob;
+ BLOBHASH *pHash;
+
+ IfFailRet(GetBlobWithSizePrefix(nOffset, &blob));
+
+ // Add the blob to the hash table.
+ if ((pHash = m_Hash.Add(blob.GetDataPointer())) == NULL)
+ {
+ Uninit();
+ return E_OUTOFMEMORY;
+ }
+ pHash->iOffset = nOffset;
+
+ nOffset += blob.GetSize();
+ }
+ return S_OK;
+} // StgBlobPool::InitOnMem
+
+//*****************************************************************************
+// Clears the hash table then calls the base class.
+//*****************************************************************************
+void StgBlobPool::Uninit()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Clear the hash table.
+ m_Hash.Clear();
+
+ // Let base class clean up.
+ StgPool::Uninit();
+} // StgBlobPool::Uninit
+
+
+//*****************************************************************************
+// The blob will be added to the pool. The offset of the blob in the pool
+// is returned in *piOffset. If the blob is already in the pool, then the
+// offset will be to the existing copy of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::AddBlob(
+ const MetaData::DataBlob *pData,
+ UINT32 *pnOffset) // Return offset of blob here.
+{
+ BLOBHASH *pHash; // Hash item for add.
+ void *pBytes; // Working pointer.
+ BYTE *pStartLoc; // Location to write real blob
+ ULONG iRequired; // How much buffer for this blob?
+ ULONG iFillerLen; // space to fill to make byte-aligned
+ HRESULT hr;
+
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ // Can we handle this blob?
+ if (pData->GetSize() > CPackedLen::MAX_LEN)
+ return (PostError(CLDB_E_TOO_BIG));
+
+ // worst case is we need three more bytes to ensure byte-aligned, hence the 3
+ iRequired = pData->GetSize() + CPackedLen::Size(pData->GetSize()) + 3;
+ if (iRequired > GetCbSegAvailable())
+ {
+ if (!Grow(iRequired))
+ return (PostError(OutOfMemory()));
+ }
+
+ // unless changed due to alignment, the location of the blob is just
+ // the value returned by GetNextLocation(), which is also a iFillerLen of
+ // 0
+
+ pStartLoc = (BYTE *)GetNextLocation();
+ iFillerLen = 0;
+
+ // technichally, only the data portion must be DWORD-aligned. So, if the
+ // data length is zero, we don't need to worry about alignment.
+
+ // Pack in the length at pStartLoc (the start location)
+ pBytes = CPackedLen::PutLength(pStartLoc, pData->GetSize());
+
+ // Put the bytes themselves.
+ memcpy(pBytes, pData->GetDataPointer(), pData->GetSize());
+
+ // Find or add the entry.
+ if ((pHash = m_Hash.Find(GetNextLocation() + iFillerLen, true)) == NULL)
+ return (PostError(OutOfMemory()));
+
+ // If the entry was new, keep the new blob.
+ if (pHash->iOffset == 0xffffffff)
+ {
+ // this blob's offset is increased by iFillerLen bytes
+ pHash->iOffset = *pnOffset = GetNextOffset() + iFillerLen;
+ // only SegAllocate what we actually used, rather than what we requested
+ SegAllocate(pData->GetSize() + CPackedLen::Size(pData->GetSize()) + iFillerLen);
+
+ // Check for hash chains that are too long.
+ if (m_Hash.MaxChainLength() > MAX_CHAIN_LENGTH)
+ {
+ IfFailRet(RehashBlobs());
+ }
+ }
+ // Else use the old one.
+ else
+ {
+ *pnOffset = pHash->iOffset;
+ }
+
+ return S_OK;
+} // StgBlobPool::AddBlob
+
+//*****************************************************************************
+// Return a pointer to a blob, and the size of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+
+ if (nOffset == 0)
+ {
+ // TODO: It would be nice to remove it, but people read behind the end of buffer,
+ // e.g. VBC reads 2 zeros even though the size is 0 when it's storing string in the blob.
+ // Nice to have: Move this to the public API only as a compat layer.
+ pData->Init((BYTE *)m_zeros, 0);
+ return S_OK;
+ }
+
+ IfFailGo(StgPool::GetData(nOffset, pData));
+
+ UINT32 cbBlobContentSize;
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPool::GetBlob
+
+//*****************************************************************************
+// Return a pointer to a blob, and the size of the blob.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::GetBlobWithSizePrefix(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+
+ if (nOffset == 0)
+ {
+ // TODO: Should be a static empty blob once we get rid of m_zeros
+ pData->Init((BYTE *)m_zeros, 1);
+ return S_OK;
+ }
+
+ IfFailGo(StgPool::GetData(nOffset, pData));
+
+ UINT32 cbBlobContentSize;
+ UINT32 cbBlobSizePrefixSize;
+ if (!pData->PeekCompressedU(&cbBlobContentSize, &cbBlobSizePrefixSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ //_ASSERTE(cbBlobSizePrefixSize <= 4);
+ //_ASSERTE(cbBlobContentSize <= CompressedInteger::const_Max);
+
+ // Cannot overflow, because previous asserts hold (in comments)
+ UINT32 cbBlobSize;
+ cbBlobSize = cbBlobContentSize + cbBlobSizePrefixSize;
+ if (!pData->TruncateToExactSize(cbBlobSize))
+ {
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPool::GetBlob
+
+//*****************************************************************************
+// Turn hashing off or on. If you turn hashing on, then any existing data is
+// thrown away and all data is rehashed during this call.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::SetHash(int bHash)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ // If turning on hash again, need to rehash all Blobs.
+ if (bHash)
+ hr = RehashBlobs();
+
+ //<TODO>@todo: m_bHash = bHash;</TODO>
+ return (hr);
+} // StgBlobPool::SetHash
+
+//*****************************************************************************
+// Clears out the existing hash table used to eliminate duplicates. Then
+// rebuilds the hash table from scratch based on the current data.
+//*****************************************************************************
+__checkReturn
+HRESULT
+StgBlobPool::RehashBlobs()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ void const *pBlob; // Pointer to a given blob.
+ ULONG cbBlob; // Length of a blob.
+ int iSizeLen = 0; // Size of an encoded length.
+ ULONG iOffset; // Location within iteration.
+ ULONG iMax; // End of loop.
+ ULONG iSeg; // Location within segment.
+ StgPoolSeg *pSeg = this; // To loop over segments.
+ BLOBHASH *pHash; // Hash item for add.
+ int iBuckets; // Buckets in the hash.
+ int iCount; // Items in the hash.
+ int iNewBuckets; // New count of buckets in the hash.
+
+ // Determine the new bucket size.
+ iBuckets = m_Hash.Buckets();
+ iCount = m_Hash.Count();
+ iNewBuckets = max(iCount, iBuckets+iBuckets/2+1);
+
+ // Remove any stale data.
+ m_Hash.Clear();
+ m_Hash.SetBuckets(iNewBuckets);
+
+ // How far should the loop go.
+ iMax = GetNextOffset();
+
+ // Go through each string, skipping initial empty string.
+ for (iSeg=iOffset=0; iOffset < iMax; )
+ {
+ // Get the string from the pool.
+ pBlob = pSeg->m_pSegData + iSeg;
+
+ cbBlob = CPackedLen::GetLength(pBlob, &iSizeLen);
+ if (cbBlob == (ULONG)-1)
+ { // Invalid blob size encoding
+
+ //#GarbageInBlobHeap
+ // Note that this is allowed in ECMA spec (see chapter "#US and #Blob heaps"):
+ // Both these heaps can contain garbage, as long as any part that is reachable from any of
+ // the tables contains a valid 'blob'.
+
+ // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
+ return S_OK;
+ }
+ //_ASSERTE((iSizeLen >= 1) && (iSizeLen <= 4) && (cbBlob <= 0x1fffffff));
+
+ // Make it blob size incl. its size encoding (cannot integer overflow)
+ cbBlob += iSizeLen;
+ // Check for integer overflow and that the entire blob entry is in this segment
+ if ((iSeg > (iSeg + cbBlob)) || ((iSeg + cbBlob) > pSeg->m_cbSegNext))
+ { // Invalid blob size
+
+ // See code:#GarbageInBlobHeap
+ // The hash is incomplete, which means that we might emit duplicate blob entries ... that is fine
+ return S_OK;
+ }
+
+ // Add the blob to the hash table.
+ if ((pHash = m_Hash.Add(pBlob)) == 0)
+ {
+ Uninit();
+ return (E_OUTOFMEMORY);
+ }
+ pHash->iOffset = iOffset;
+
+ // Move to next blob.
+ iOffset += cbBlob;
+ iSeg += cbBlob;
+ if (iSeg >= pSeg->m_cbSegNext)
+ {
+ pSeg = pSeg->m_pNextSeg;
+ iSeg = 0;
+ }
+ }
+ return (S_OK);
+} // StgBlobPool::RehashBlobs
+
+
+//
+// CInMemoryStream
+//
+
+
+ULONG
+STDMETHODCALLTYPE CInMemoryStream::Release()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ SUPPORTS_DAC_HOST_ONLY;
+ }
+ CONTRACTL_END
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ {
+ if (m_dataCopy != NULL)
+ delete [] m_dataCopy;
+
+ delete this;
+ }
+ return (cRef);
+} // CInMemoryStream::Release
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::QueryInterface(REFIID riid, PVOID *ppOut)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ if (!ppOut)
+ {
+ return E_POINTER;
+ }
+
+ *ppOut = NULL;
+ if (riid == IID_IStream || riid == IID_ISequentialStream || riid == IID_IUnknown)
+ {
+ *ppOut = this;
+ AddRef();
+ return (S_OK);
+ }
+
+ return E_NOINTERFACE;
+
+} // CInMemoryStream::QueryInterface
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ ULONG cbRead = min(cb, m_cbSize - m_cbCurrent);
+
+ if (cbRead == 0)
+ return (S_FALSE);
+ memcpy(pv, (void *) ((ULONG_PTR) m_pMem + m_cbCurrent), cbRead);
+ if (pcbRead)
+ *pcbRead = cbRead;
+ m_cbCurrent += cbRead;
+ return (S_OK);
+} // CInMemoryStream::Read
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ if (ovadd_gt(m_cbCurrent, cb, m_cbSize))
+ return (OutOfMemory());
+
+ memcpy((BYTE *) m_pMem + m_cbCurrent, pv, cb);
+ m_cbCurrent += cb;
+ if (pcbWritten) *pcbWritten = cb;
+ return (S_OK);
+} // CInMemoryStream::Write
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ _ASSERTE(dwOrigin == STREAM_SEEK_SET || dwOrigin == STREAM_SEEK_CUR);
+ _ASSERTE(dlibMove.QuadPart <= static_cast<LONGLONG>(UINT32_MAX));
+
+ if (dwOrigin == STREAM_SEEK_SET)
+ {
+ m_cbCurrent = (ULONG) dlibMove.QuadPart;
+ }
+ else
+ if (dwOrigin == STREAM_SEEK_CUR)
+ {
+ m_cbCurrent+= (ULONG)dlibMove.QuadPart;
+ }
+
+ if (plibNewPosition)
+ {
+ plibNewPosition->QuadPart = m_cbCurrent;
+ }
+
+ return (m_cbCurrent < m_cbSize) ? (S_OK) : E_FAIL;
+} // CInMemoryStream::Seek
+
+HRESULT
+STDMETHODCALLTYPE
+CInMemoryStream::CopyTo(
+ IStream *pstm,
+ ULARGE_INTEGER cb,
+ ULARGE_INTEGER *pcbRead,
+ ULARGE_INTEGER *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY;
+
+ HRESULT hr;
+ // We don't handle pcbRead or pcbWritten.
+ _ASSERTE(pcbRead == 0);
+ _ASSERTE(pcbWritten == 0);
+
+ _ASSERTE(cb.QuadPart <= UINT32_MAX);
+ ULONG cbTotal = min(static_cast<ULONG>(cb.QuadPart), m_cbSize - m_cbCurrent);
+ ULONG cbRead=min(1024, cbTotal);
+ CQuickBytes rBuf;
+ void *pBuf = rBuf.AllocNoThrow(cbRead);
+ if (pBuf == 0)
+ return (PostError(OutOfMemory()));
+
+ while (cbTotal)
+ {
+ if (cbRead > cbTotal)
+ cbRead = cbTotal;
+ if (FAILED(hr=Read(pBuf, cbRead, 0)))
+ return (hr);
+ if (FAILED(hr=pstm->Write(pBuf, cbRead, 0)))
+ return (hr);
+ cbTotal -= cbRead;
+ }
+
+ // Adjust seek pointer to the end.
+ m_cbCurrent = m_cbSize;
+
+ return (S_OK);
+} // CInMemoryStream::CopyTo
+
+HRESULT
+CInMemoryStream::CreateStreamOnMemory(
+ void *pMem, // Memory to create stream on.
+ ULONG cbSize, // Size of data.
+ IStream **ppIStream, // Return stream object here.
+ BOOL fDeleteMemoryOnRelease)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CInMemoryStream *pIStream; // New stream object.
+ if ((pIStream = new (nothrow) CInMemoryStream) == 0)
+ return (PostError(OutOfMemory()));
+ pIStream->InitNew(pMem, cbSize);
+ if (fDeleteMemoryOnRelease)
+ {
+ // make sure this memory is allocated using new
+ pIStream->m_dataCopy = (BYTE *)pMem;
+ }
+ *ppIStream = pIStream;
+ return (S_OK);
+} // CInMemoryStream::CreateStreamOnMemory
+
+HRESULT
+CInMemoryStream::CreateStreamOnMemoryCopy(
+ void *pMem,
+ ULONG cbSize,
+ IStream **ppIStream)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY;);
+ }
+ CONTRACTL_END
+
+ CInMemoryStream *pIStream; // New stream object.
+ if ((pIStream = new (nothrow) CInMemoryStream) == 0)
+ return (PostError(OutOfMemory()));
+
+ // Init the stream.
+ pIStream->m_cbCurrent = 0;
+ pIStream->m_cbSize = cbSize;
+
+ // Copy the data.
+ pIStream->m_dataCopy = new (nothrow) BYTE[cbSize];
+
+ if (pIStream->m_dataCopy == NULL)
+ {
+ delete pIStream;
+ return (PostError(OutOfMemory()));
+ }
+
+ pIStream->m_pMem = pIStream->m_dataCopy;
+ memcpy(pIStream->m_dataCopy, pMem, cbSize);
+
+ *ppIStream = pIStream;
+ return (S_OK);
+} // CInMemoryStream::CreateStreamOnMemoryCopy
+
+//---------------------------------------------------------------------------
+// CGrowableStream is a simple IStream implementation that grows as
+// its written to. All the memory is contigious, so read access is
+// fast. A grow does a realloc, so be aware of that if you're going to
+// use this.
+//---------------------------------------------------------------------------
+
+//Constructs a new GrowableStream
+// multiplicativeGrowthRate - when the stream grows it will be at least this
+// multiple of its old size. Values greater than 1 ensure O(N) amortized
+// performance growing the stream to size N, 1 ensures O(N^2) amortized perf
+// but gives the tightest memory usage. Valid range is [1.0, 2.0].
+// additiveGrowthRate - when the stream grows it will increase in size by at least
+// this number of bytes. Larger numbers cause fewer re-allocations at the cost of
+// increased memory usage.
+CGrowableStream::CGrowableStream(float multiplicativeGrowthRate, DWORD additiveGrowthRate)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ m_swBuffer = NULL;
+ m_dwBufferSize = 0;
+ m_dwBufferIndex = 0;
+ m_dwStreamLength = 0;
+ m_cRef = 1;
+
+ // Lets make sure these values stay somewhat sane... if you adjust the limits
+ // make sure you also write correct overflow checking code in EnsureCapcity
+ _ASSERTE(multiplicativeGrowthRate >= 1.0F && multiplicativeGrowthRate <= 2.0F);
+ m_multiplicativeGrowthRate = min(max(1.0F, multiplicativeGrowthRate), 2.0F);
+
+ _ASSERTE(additiveGrowthRate >= 1);
+ m_additiveGrowthRate = max(1, additiveGrowthRate);
+} // CGrowableStream::CGrowableStream
+
+#ifndef DACCESS_COMPILE
+
+CGrowableStream::~CGrowableStream()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END
+
+ // Destroy the buffer.
+ if (m_swBuffer != NULL)
+ delete [] m_swBuffer;
+
+ m_swBuffer = NULL;
+ m_dwBufferSize = 0;
+} // CGrowableStream::~CGrowableStream
+
+// Grows the stream and optionally the internal buffer to ensure it is at least
+// newLogicalSize
+HRESULT CGrowableStream::EnsureCapacity(DWORD newLogicalSize)
+{
+ _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
+
+ // If there is no enough space left in the buffer, grow it
+ if (newLogicalSize > m_dwBufferSize)
+ {
+ // Grow to max of newLogicalSize, m_dwBufferSize*multiplicativeGrowthRate, and
+ // m_dwBufferSize+m_additiveGrowthRate
+ S_UINT32 addSize = S_UINT32(m_dwBufferSize) + S_UINT32(m_additiveGrowthRate);
+ if (addSize.IsOverflow())
+ {
+ addSize = S_UINT32(UINT_MAX);
+ }
+
+ // this should have been enforced in the constructor too
+ _ASSERTE(m_multiplicativeGrowthRate <= 2.0 && m_multiplicativeGrowthRate >= 1.0);
+
+ // 2*UINT_MAX doesn't overflow a float so this certain to be safe
+ float multSizeF = (float)m_dwBufferSize * m_multiplicativeGrowthRate;
+ DWORD multSize;
+ if(multSizeF > (float)UINT_MAX)
+ {
+ multSize = UINT_MAX;
+ }
+ else
+ {
+ multSize = (DWORD)multSizeF;
+ }
+
+ DWORD newBufferSize = max(max(newLogicalSize, multSize), addSize.Value());
+
+ char *tmp = new (nothrow) char[newBufferSize];
+ if(tmp == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ if (m_swBuffer) {
+ memcpy (tmp, m_swBuffer, m_dwBufferSize);
+ delete [] m_swBuffer;
+ }
+ m_swBuffer = (BYTE *)tmp;
+ m_dwBufferSize = newBufferSize;
+ }
+
+ _ASSERTE(m_dwBufferSize >= newLogicalSize);
+ // the internal buffer is big enough, might have to increase logical size
+ // though
+ if(newLogicalSize > m_dwStreamLength)
+ {
+ m_dwStreamLength = newLogicalSize;
+ }
+
+ _ASSERTE(m_dwBufferSize >= m_dwStreamLength);
+ return S_OK;
+}
+
+ULONG
+STDMETHODCALLTYPE
+CGrowableStream::Release()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+
+ if (cRef == 0)
+ delete this;
+
+ return cRef;
+} // CGrowableStream::Release
+
+HRESULT
+STDMETHODCALLTYPE
+CGrowableStream::QueryInterface(
+ REFIID riid,
+ PVOID *ppOut)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (riid != IID_IUnknown && riid!=IID_ISequentialStream && riid!=IID_IStream)
+ return E_NOINTERFACE;
+
+ *ppOut = this;
+ AddRef();
+ return (S_OK);
+} // CGrowableStream::QueryInterface
+
+HRESULT
+CGrowableStream::Read(
+ void *pv,
+ ULONG cb,
+ ULONG *pcbRead)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ HRESULT hr = S_OK;
+ DWORD dwCanReadBytes = 0;
+
+ if (NULL == pv)
+ return E_POINTER;
+
+ // short-circuit a zero-length read or see if we are at the end
+ if (cb == 0 || m_dwBufferIndex >= m_dwStreamLength)
+ {
+ if (pcbRead != NULL)
+ *pcbRead = 0;
+
+ return S_OK;
+ }
+
+ // Figure out if we have enough room in the stream (excluding any
+ // unused space at the end of the buffer)
+ dwCanReadBytes = cb;
+
+ S_UINT32 dwNewIndex = S_UINT32(dwCanReadBytes) + S_UINT32(m_dwBufferIndex);
+ if (dwNewIndex.IsOverflow() || (dwNewIndex.Value() > m_dwStreamLength))
+ {
+ // Only read whatever is left in the buffer (if any)
+ dwCanReadBytes = (m_dwStreamLength - m_dwBufferIndex);
+ }
+
+ // copy from our buffer to caller's buffer
+ memcpy(pv, &m_swBuffer[m_dwBufferIndex], dwCanReadBytes);
+
+ // adjust our current position
+ m_dwBufferIndex += dwCanReadBytes;
+
+ // if they want the info, tell them how many byte we read for them
+ if (pcbRead != NULL)
+ *pcbRead = dwCanReadBytes;
+
+ return hr;
+} // CGrowableStream::Read
+
+HRESULT
+CGrowableStream::Write(
+ const void *pv,
+ ULONG cb,
+ ULONG *pcbWritten)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ HRESULT hr = S_OK;
+ DWORD dwActualWrite = 0;
+
+ // avoid NULL write
+ if (cb == 0)
+ {
+ hr = S_OK;
+ goto Error;
+ }
+
+ // Check if our buffer is large enough
+ _ASSERTE(m_dwBufferIndex <= m_dwStreamLength);
+ _ASSERTE(m_dwStreamLength <= m_dwBufferSize);
+
+ // If there is no enough space left in the buffer, grow it
+ if (cb > (m_dwStreamLength - m_dwBufferIndex))
+ {
+ // Determine the new size needed
+ S_UINT32 size = S_UINT32(m_dwBufferSize) + S_UINT32(cb);
+ if (size.IsOverflow())
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ goto Error;
+ }
+
+ hr = EnsureCapacity(size.Value());
+ if(FAILED(hr))
+ {
+ goto Error;
+ }
+ }
+
+ if ((pv != NULL) && (cb > 0))
+ {
+ // write to current position in the buffer
+ memcpy(&m_swBuffer[m_dwBufferIndex], pv, cb);
+
+ // now update our current index
+ m_dwBufferIndex += cb;
+
+ // in case they want to know the number of bytes written
+ dwActualWrite = cb;
+ }
+
+Error:
+ if (pcbWritten)
+ *pcbWritten = dwActualWrite;
+
+ return hr;
+} // CGrowableStream::Write
+
+STDMETHODIMP
+CGrowableStream::Seek(
+ LARGE_INTEGER dlibMove,
+ DWORD dwOrigin,
+ ULARGE_INTEGER *plibNewPosition)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ // a Seek() call on STREAM_SEEK_CUR and a dlibMove == 0 is a
+ // request to get the current seek position.
+ if ((dwOrigin == STREAM_SEEK_CUR && dlibMove.u.LowPart == 0) &&
+ (dlibMove.u.HighPart == 0) &&
+ (NULL != plibNewPosition))
+ {
+ goto Error;
+ }
+
+ // we only support STREAM_SEEK_SET (beginning of buffer)
+ if (dwOrigin != STREAM_SEEK_SET)
+ return E_NOTIMPL;
+
+ // did they ask to seek past end of stream? If so we're supposed to
+ // extend with zeros. But we've never supported that.
+ if (dlibMove.u.LowPart > m_dwStreamLength)
+ return E_UNEXPECTED;
+
+ // we ignore the high part of the large integer
+ SIMPLIFYING_ASSUMPTION(dlibMove.u.HighPart == 0);
+ m_dwBufferIndex = dlibMove.u.LowPart;
+
+Error:
+ if (NULL != plibNewPosition)
+ {
+ plibNewPosition->u.HighPart = 0;
+ plibNewPosition->u.LowPart = m_dwBufferIndex;
+ }
+
+ return S_OK;
+} // CGrowableStream::Seek
+
+STDMETHODIMP
+CGrowableStream::SetSize(
+ ULARGE_INTEGER libNewSize)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ DWORD dwNewSize = libNewSize.u.LowPart;
+
+ _ASSERTE(libNewSize.u.HighPart == 0);
+
+ // we don't support large allocations
+ if (libNewSize.u.HighPart > 0)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = EnsureCapacity(dwNewSize);
+ if(FAILED(hr))
+ {
+ return hr;
+ }
+
+ // EnsureCapacity doesn't shrink the logicalSize if dwNewSize is smaller
+ // and SetSize is allowed to shrink the stream too. Note that we won't
+ // release physical memory here, we just appear to get smaller
+ m_dwStreamLength = dwNewSize;
+
+ return S_OK;
+} // CGrowableStream::SetSize
+
+STDMETHODIMP
+CGrowableStream::Stat(
+ STATSTG *pstatstg,
+ DWORD grfStatFlag)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (NULL == pstatstg)
+ return E_POINTER;
+
+ // this is the only useful information we hand out - the length of the stream
+ pstatstg->cbSize.u.HighPart = 0;
+ pstatstg->cbSize.u.LowPart = m_dwStreamLength;
+ pstatstg->type = STGTY_STREAM;
+
+ // we ignore the grfStatFlag - we always assume STATFLAG_NONAME
+ pstatstg->pwcsName = NULL;
+
+ pstatstg->grfMode = 0;
+ pstatstg->grfLocksSupported = 0;
+ pstatstg->clsid = CLSID_NULL;
+ pstatstg->grfStateBits = 0;
+
+ return S_OK;
+} // CGrowableStream::Stat
+
+//
+// Clone - Make a deep copy of the stream into a new cGrowableStream instance
+//
+// Arguments:
+// ppStream - required output parameter for the new stream instance
+//
+// Returns:
+// S_OK on succeess, or an error code on failure.
+//
+HRESULT
+CGrowableStream::Clone(
+ IStream **ppStream)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT; //E_OUTOFMEMORY
+
+ if (NULL == ppStream)
+ return E_POINTER;
+
+ // Copy our entire buffer into the new stream
+ CGrowableStream * newStream = new (nothrow) CGrowableStream();
+ if (newStream == NULL)
+ {
+ return E_OUTOFMEMORY;
+ }
+
+ HRESULT hr = newStream->Write(m_swBuffer, m_dwStreamLength, NULL);
+ if (FAILED(hr))
+ {
+ delete newStream;
+ return hr;
+ }
+
+ *ppStream = newStream;
+ return S_OK;
+} // CGrowableStream::Clone
+
+#endif // !DACCESS_COMPILE
diff --git a/src/coreclr/utilcode/stgpooli.cpp b/src/coreclr/utilcode/stgpooli.cpp
new file mode 100644
index 00000000000..5e56835bc44
--- /dev/null
+++ b/src/coreclr/utilcode/stgpooli.cpp
@@ -0,0 +1,347 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// StgPool.cpp
+//
+
+//
+// Pools are used to reduce the amount of data actually required in the database.
+// This allows for duplicate string and binary values to be folded into one
+// copy shared by the rest of the database. Strings are tracked in a hash
+// table when insert/changing data to find duplicates quickly. The strings
+// are then persisted consecutively in a stream in the database format.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+
+int CStringPoolHash::Cmp(
+ const void *pData, // A string.
+ void *pItem) // A hash item which refers to a string.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ LPCSTR p1 = reinterpret_cast<LPCSTR>(pData);
+ LPCSTR p2;
+ if (FAILED(m_Pool->GetString(reinterpret_cast<STRINGHASH*>(pItem)->iOffset, &p2)))
+ {
+ return -1;
+ }
+ return (strcmp(p1, p2));
+} // int CStringPoolHash::Cmp()
+
+
+int CBlobPoolHash::Cmp(
+ const void *pData, // A blob.
+ void *pItem) // A hash item which refers to a blob.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ ULONG ul1;
+ ULONG ul2;
+ MetaData::DataBlob data2;
+
+ // Get size of first item.
+ ul1 = CPackedLen::GetLength(pData);
+ // Adjust size to include the length of size field.
+ ul1 += CPackedLen::Size(ul1);
+
+ // Get the second item.
+ if (FAILED(m_Pool->GetData(reinterpret_cast<BLOBHASH*>(pItem)->iOffset, &data2)))
+ {
+ return -1;
+ }
+
+ // Get and adjust size of second item.
+ ul2 = CPackedLen::GetLength(data2.GetDataPointer());
+ ul2 += CPackedLen::Size(ul2);
+
+ if (ul1 < ul2)
+ return (-1);
+ else if (ul1 > ul2)
+ return (1);
+ return (memcmp(pData, data2.GetDataPointer(), ul1));
+} // int CBlobPoolHash::Cmp()
+
+int CGuidPoolHash::Cmp(const void *pData, void *pItem)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ GUID *p2;
+ if (FAILED(m_Pool->GetGuid(reinterpret_cast<GUIDHASH*>(pItem)->iIndex, &p2)))
+ {
+ return -1;
+ }
+ return (memcmp(pData, p2, sizeof(GUID)));
+} // int CGuidPoolHash::Cmp()
+
+//
+//
+// CPackedLen
+//
+//
+
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+void const *CPackedLen::GetData( // Pointer to data, or 0 on error.
+ void const *pData, // First byte of length.
+ ULONG *pLength) // Put length here, or -1 on error.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ *pLength = (*pBytes & 0x7f);
+ return pBytes + 1;
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ return pBytes + 2;
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ return pBytes + 4;
+ }
+
+ *pLength = (ULONG) -1;
+ return 0;
+} // void const *CPackedLen::GetData()
+
+#ifndef MAX_PTR
+#define MAX_PTR ((BYTE*)(~(SSIZE_T)0))
+#endif
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetLength( // S_OK, or error
+ void const *pDataSource, // First byte of length.
+ void const *pDataSourceEnd, // End of valid source data memory
+ ULONG *pLength, // Length of data, if return S_OK
+ void const **ppDataNext) // Pointer immediately following encoded length
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ if (pDataSource == NULL ||
+ pDataSourceEnd == NULL ||
+ pDataSourceEnd < pDataSource ||
+ ppDataNext == NULL ||
+ pLength == NULL ||
+ pDataSource > (MAX_PTR - 4))
+ {
+ return E_INVALIDARG;
+ }
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pDataSource);
+ BYTE const *pBytesEnd = reinterpret_cast<BYTE const*>(pDataSourceEnd);
+
+ size_t cbAvail = pBytesEnd - pBytes;
+
+ if (cbAvail < 1)
+ { // Fail if no source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ *pLength = (*pBytes & 0x7f);
+ *ppDataNext = pBytes + 1;
+ return S_OK;
+ }
+
+ if (cbAvail < 2)
+ { // Fail if not enough source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ *pLength = ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ *ppDataNext = pBytes + 2;
+ return S_OK;
+ }
+
+ if (cbAvail < 4)
+ { // Fail if not enough source data available
+ return COR_E_OVERFLOW;
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ *pLength = ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ *ppDataNext = pBytes + 4;
+ return S_OK;;
+ }
+
+ return COR_E_OVERFLOW;
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetData( // S_OK, or error
+ void const *pDataSource, // First byte of length.
+ void const *pDataSourceEnd, // End of valid source data memory
+ ULONG *pcbData, // Length of data
+ void const **ppData) // Start of data
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr = S_OK;
+
+ IfFailRet(SafeGetLength(pDataSource, pDataSourceEnd, pcbData, ppData));
+
+ if (*pcbData == 0)
+ { // Zero length value means zero data, so no range checking required.
+ return S_OK;
+ }
+
+ BYTE const *pbData = reinterpret_cast<BYTE const*>(*ppData);
+
+ if (pbData + *pcbData < pbData)
+ { // First check for integer overflow
+ return COR_E_OVERFLOW;
+ }
+
+ if (pDataSourceEnd < pbData + *pcbData)
+ { // Now check for data buffer overflow
+ return COR_E_OVERFLOW;
+ }
+
+ return S_OK;
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the data, store length.
+//*****************************************************************************
+HRESULT CPackedLen::SafeGetData( // S_OK, or error
+ void const *pDataSource, // First byte of data
+ ULONG cbDataSource, // Count of valid bytes in data source
+ ULONG *pcbData, // Length of data
+ void const **ppData) // Start of data
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ return SafeGetData(pDataSource, (void const *)((BYTE const *)pDataSource + cbDataSource), pcbData, ppData);
+} // CPackedLen::GetLength
+
+//*****************************************************************************
+// Parse a length, return the length, pointer to actual bytes.
+//*****************************************************************************
+ULONG CPackedLen::GetLength( // Length or -1 on error.
+ void const *pData, // First byte of length.
+ void const **ppCode) // Put pointer to bytes here, if not 0.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ if (ppCode) *ppCode = pBytes + 1;
+ return (*pBytes & 0x7f);
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ if (ppCode) *ppCode = pBytes + 2;
+ return ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ if (ppCode) *ppCode = pBytes + 4;
+ return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ }
+
+ return (ULONG) -1;
+} // ULONG CPackedLen::GetLength()
+
+//*****************************************************************************
+// Parse a length, return the length, size of the length.
+//*****************************************************************************
+ULONG CPackedLen::GetLength( // Length or -1 on error.
+ void const *pData, // First byte of length.
+ int *pSizeLen) // Put size of length here, if not 0.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE const *pBytes = reinterpret_cast<BYTE const*>(pData);
+
+ if ((*pBytes & 0x80) == 0x00) // 0??? ????
+ {
+ if (pSizeLen) *pSizeLen = 1;
+ return (*pBytes & 0x7f);
+ }
+
+ if ((*pBytes & 0xC0) == 0x80) // 10?? ????
+ {
+ if (pSizeLen) *pSizeLen = 2;
+ return ((*pBytes & 0x3f) << 8 | *(pBytes+1));
+ }
+
+ if ((*pBytes & 0xE0) == 0xC0) // 110? ????
+ {
+ if (pSizeLen) *pSizeLen = 4;
+ return ((*pBytes & 0x1f) << 24 | *(pBytes+1) << 16 | *(pBytes+2) << 8 | *(pBytes+3));
+ }
+
+ return (ULONG) -1;
+} // ULONG CPackedLen::GetLength()
+
+//*****************************************************************************
+// Encode a length.
+//*****************************************************************************
+#ifdef _MSC_VER
+#pragma warning(disable:4244) // conversion from unsigned long to unsigned char
+#endif
+void* CPackedLen::PutLength( // First byte past length.
+ void *pData, // Pack the length here.
+ ULONG iLen) // The length.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ BYTE *pBytes = reinterpret_cast<BYTE*>(pData);
+
+ if (iLen <= 0x7F)
+ {
+ *pBytes = iLen;
+ return pBytes + 1;
+ }
+
+ if (iLen <= 0x3FFF)
+ {
+ *pBytes = (iLen >> 8) | 0x80;
+ *(pBytes+1) = iLen & 0xFF;
+ return pBytes + 2;
+ }
+
+ _ASSERTE(iLen <= 0x1FFFFFFF);
+ *pBytes = (iLen >> 24) | 0xC0;
+ *(pBytes+1) = (iLen >> 16) & 0xFF;
+ *(pBytes+2) = (iLen >> 8) & 0xFF;
+ *(pBytes+3) = iLen & 0xFF;
+ return pBytes + 4;
+} // void* CPackedLen::PutLength()
+#ifdef _MSC_VER
+#pragma warning(default:4244) // conversion from unsigned long to unsigned char
+#endif
+
diff --git a/src/coreclr/utilcode/stgpoolreadonly.cpp b/src/coreclr/utilcode/stgpoolreadonly.cpp
new file mode 100644
index 00000000000..439e9ca42d2
--- /dev/null
+++ b/src/coreclr/utilcode/stgpoolreadonly.cpp
@@ -0,0 +1,210 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// StgPoolReadOnly.cpp
+//
+
+//
+// Read only pools are used to reduce the amount of data actually required in the database.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard include.
+#include <stgpool.h> // Our interface definitions.
+#include "metadatatracker.h"
+//
+//
+// StgPoolReadOnly
+//
+//
+
+#if METADATATRACKER_ENABLED
+MetaDataTracker *MetaDataTracker::m_MDTrackers = NULL;
+BOOL MetaDataTracker::s_bEnabled = FALSE;
+
+void (*MetaDataTracker::s_IBCLogMetaDataAccess)(const void *addr) = NULL;
+void (*MetaDataTracker::s_IBCLogMetaDataSearch)(const void *result) = NULL;
+
+#endif // METADATATRACKER_ENABLED
+
+const BYTE StgPoolSeg::m_zeros[64] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+
+//*****************************************************************************
+// Free any memory we allocated.
+//*****************************************************************************
+StgPoolReadOnly::~StgPoolReadOnly()
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+
+//*****************************************************************************
+// Init the pool from existing data.
+//*****************************************************************************
+HRESULT StgPoolReadOnly::InitOnMemReadOnly(// Return code.
+ void *pData, // Predefined data.
+ ULONG iSize) // Size of data.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return E_OUTOFMEMORY);
+ }
+ CONTRACTL_END
+
+ // Make sure we aren't stomping anything and are properly initialized.
+ _ASSERTE(m_pSegData == m_zeros);
+
+ // Create case requires no further action.
+ if (pData == NULL)
+ return E_INVALIDARG;
+
+ // Keep m_zeros data pointer if there's no content of the pool
+ if (iSize != 0)
+ {
+ m_pSegData = reinterpret_cast<BYTE*>(pData);
+ }
+ m_cbSegSize = iSize;
+ m_cbSegNext = iSize;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Prepare to shut down or reinitialize.
+//*****************************************************************************
+void StgPoolReadOnly::Uninit()
+{
+ LIMITED_METHOD_CONTRACT;
+
+ m_pSegData = (BYTE*)m_zeros;
+ m_pNextSeg = 0;
+}
+
+
+//*****************************************************************************
+// Convert a string to UNICODE into the caller's buffer.
+//*****************************************************************************
+HRESULT StgPoolReadOnly::GetStringW( // Return code.
+ ULONG iOffset, // Offset of string in pool.
+ __out_ecount(cchBuffer) LPWSTR szOut, // Output buffer for string.
+ int cchBuffer) // Size of output buffer.
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FAULT;
+
+ HRESULT hr;
+ LPCSTR pString; // The string in UTF8.
+ int iChars;
+
+ IfFailRet(GetString(iOffset, &pString));
+ iChars = ::WszMultiByteToWideChar(CP_UTF8, 0, pString, -1, szOut, cchBuffer);
+ if (iChars == 0)
+ return (BadError(HRESULT_FROM_NT(GetLastError())));
+ return S_OK;
+}
+
+//*****************************************************************************
+// Return a pointer to a null terminated blob given an offset previously
+// handed out by Addblob or Findblob.
+//*****************************************************************************
+HRESULT
+StgPoolReadOnly::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+ UINT32 cbBlobContentSize;
+
+ // This should not be a necessary special case. The zero byte at the
+ // start of the pool will code for a length of zero. We will return
+ // a pointer to the next length byte, but the caller should notice that
+ // the size is zero, and should not look at any bytes.
+ // [SL] Yes, but we don't need all further computations and checks if iOffset==0
+
+ if (nOffset == 0)
+ {
+ pData->Clear();
+ return S_OK;
+ }
+
+ // Is the offset within this heap?
+ if (!IsValidOffset(nOffset))
+ {
+ Debug_ReportError("Invalid blob offset.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ IfFailGo(GetDataReadOnly(nOffset, pData));
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - size compression.");
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - reaches behind the end of data block.");
+ IfFailGo(COR_E_BADIMAGEFORMAT);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgPoolReadOnly::GetBlob
+
+//*****************************************************************************
+// code:StgPoolReadOnly::GetBlob specialization with inlined check for valid offsets to avoid redundant code:StgPoolReadOnly::GetDataReadOnly calls.
+// code:StgPoolReadOnly::GetDataReadOnly is not cheap because of it performs binary lookup in hot metadata.
+//*****************************************************************************
+HRESULT
+StgBlobPoolReadOnly::GetBlob(
+ UINT32 nOffset, // Offset of blob in pool.
+ MetaData::DataBlob *pData)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ HRESULT hr;
+ UINT32 cbBlobContentSize;
+
+ // This should not be a necessary special case. The zero byte at the
+ // start of the pool will code for a length of zero. We will return
+ // a pointer to the next length byte, but the caller should notice that
+ // the size is zero, and should not look at any bytes.
+ // [SL] Yes, but we don't need all further computations and checks if iOffset==0
+
+ if (nOffset == 0)
+ {
+ pData->Clear();
+ return S_OK;
+ }
+
+ if (m_pSegData == m_zeros)
+ {
+ Debug_ReportError("Invalid blob offset.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ IfFailGo(GetDataReadOnly(nOffset, pData));
+ if (!pData->GetCompressedU(&cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - size compression.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+ if (!pData->TruncateToExactSize(cbBlobContentSize))
+ {
+ Debug_ReportError("Invalid blob - reaches behind the end of data block.");
+ IfFailGo(CLDB_E_INDEX_NOTFOUND);
+ }
+
+ return S_OK;
+ErrExit:
+ pData->Clear();
+ return hr;
+} // StgBlobPoolReadOnly::GetBlob
diff --git a/src/coreclr/utilcode/stresslog.cpp b/src/coreclr/utilcode/stresslog.cpp
new file mode 100644
index 00000000000..108080455d7
--- /dev/null
+++ b/src/coreclr/utilcode/stresslog.cpp
@@ -0,0 +1,684 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+/*************************************************************************************/
+/* StressLog.cpp */
+/*************************************************************************************/
+
+/*************************************************************************************/
+
+#include "stdafx.h" // precompiled headers
+
+#include "switches.h"
+#include "stresslog.h"
+#include "clrhost.h"
+#define DONOT_DEFINE_ETW_CALLBACK
+#include "eventtracebase.h"
+#include "ex.h"
+
+ #if !defined(STRESS_LOG_READONLY)
+#ifdef HOST_WINDOWS
+HANDLE StressLogChunk::s_LogChunkHeap = NULL;
+#endif
+thread_local ThreadStressLog* StressLog::t_pCurrentThreadLog;
+#endif // !STRESS_LOG_READONLY
+
+/*********************************************************************************/
+#if defined(HOST_X86)
+
+/* This is like QueryPerformanceCounter but a lot faster. On machines with
+ variable-speed CPUs (for power management), this is not accurate, but may
+ be good enough.
+*/
+__forceinline __declspec(naked) unsigned __int64 getTimeStamp() {
+ STATIC_CONTRACT_LEAF;
+
+ __asm {
+ RDTSC // read time stamp counter
+ ret
+ };
+}
+
+#else // HOST_X86
+unsigned __int64 getTimeStamp() {
+ STATIC_CONTRACT_LEAF;
+
+ LARGE_INTEGER ret;
+ ZeroMemory(&ret, sizeof(LARGE_INTEGER));
+
+ QueryPerformanceCounter(&ret);
+
+ return ret.QuadPart;
+}
+
+#endif // HOST_X86
+
+#if defined(HOST_X86) && !defined(HOST_UNIX)
+
+/*********************************************************************************/
+/* Get the the frequency cooresponding to 'getTimeStamp'. For x86, this is the
+ frequency of the RDTSC instruction, which is just the clock rate of the CPU.
+ This can vary due to power management, so this is at best a rough approximation.
+*/
+unsigned __int64 getTickFrequency()
+{
+ //
+ // At startup, the OS calculates the CPU clock frequency and makes it available
+ // at HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor\0
+ //
+
+ unsigned __int64 hz = 0;
+
+ HKEY hKey;
+ if (ERROR_SUCCESS == RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ W("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
+ 0,
+ KEY_QUERY_VALUE,
+ &hKey))
+ {
+ DWORD mhz;
+ DWORD mhzType;
+ DWORD cbMhz = (DWORD)sizeof(mhz);
+ if (ERROR_SUCCESS == RegQueryValueExW(
+ hKey,
+ W("~MHz"),
+ NULL,
+ &mhzType,
+ (LPBYTE)&mhz,
+ &cbMhz))
+ {
+ _ASSERTE(REG_DWORD == mhzType);
+ _ASSERTE((DWORD)sizeof(mhz) == cbMhz);
+
+ hz = (unsigned __int64)mhz * 1000000;
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ return hz;
+}
+
+#else // HOST_X86
+
+
+/*********************************************************************************/
+/* Get the the frequency cooresponding to 'getTimeStamp'. For non-x86
+ architectures, this is just the performance counter frequency.
+*/
+unsigned __int64 getTickFrequency()
+{
+ LARGE_INTEGER ret;
+ ZeroMemory(&ret, sizeof(LARGE_INTEGER));
+ QueryPerformanceFrequency(&ret);
+ return ret.QuadPart;
+}
+
+#endif // HOST_X86
+
+#ifdef STRESS_LOG
+
+StressLog StressLog::theLog = { 0, 0, 0, 0, 0, 0, TLS_OUT_OF_INDEXES, 0, 0, 0 };
+const static unsigned __int64 RECYCLE_AGE = 0x40000000L; // after a billion cycles, we can discard old threads
+
+/*********************************************************************************/
+void StressLog::Enter(CRITSEC_COOKIE) {
+ STATIC_CONTRACT_LEAF;
+
+ IncCantAllocCount();
+ ClrEnterCriticalSection(theLog.lock);
+ DecCantAllocCount();
+}
+
+void StressLog::Leave(CRITSEC_COOKIE) {
+ STATIC_CONTRACT_LEAF;
+
+ IncCantAllocCount();
+ ClrLeaveCriticalSection(theLog.lock);
+ DecCantAllocCount();
+}
+
+/*********************************************************************************/
+void StressLog::Initialize(unsigned facilities, unsigned level, unsigned maxBytesPerThread,
+ unsigned maxBytesTotal, void* moduleBase)
+{
+ STATIC_CONTRACT_LEAF;
+
+ if (theLog.MaxSizePerThread != 0)
+ {
+ // guard ourself against multiple initialization. First init wins.
+ return;
+ }
+
+ theLog.lock = ClrCreateCriticalSection(CrstStressLog,(CrstFlags)(CRST_UNSAFE_ANYMODE|CRST_DEBUGGER_THREAD|CRST_TAKEN_DURING_SHUTDOWN));
+ // StressLog::Terminate is going to free memory.
+ if (maxBytesPerThread < STRESSLOG_CHUNK_SIZE)
+ {
+ maxBytesPerThread = STRESSLOG_CHUNK_SIZE;
+ }
+ theLog.MaxSizePerThread = maxBytesPerThread;
+
+ if (maxBytesTotal < STRESSLOG_CHUNK_SIZE * 256)
+ {
+ maxBytesTotal = STRESSLOG_CHUNK_SIZE * 256;
+ }
+ theLog.MaxSizeTotal = maxBytesTotal;
+ theLog.totalChunk = 0;
+ theLog.facilitiesToLog = facilities | LF_ALWAYS;
+ theLog.levelToLog = level;
+ theLog.deadCount = 0;
+
+ theLog.tickFrequency = getTickFrequency();
+
+ GetSystemTimeAsFileTime (&theLog.startTime);
+ theLog.startTimeStamp = getTimeStamp();
+ theLog.moduleOffset = (SIZE_T)moduleBase;
+
+#ifndef HOST_UNIX
+#ifdef _DEBUG
+ HMODULE hModNtdll = GetModuleHandleA("ntdll.dll");
+ theLog.RtlCaptureStackBackTrace = reinterpret_cast<PFNRtlCaptureStackBackTrace>(
+ GetProcAddress(hModNtdll, "RtlCaptureStackBackTrace"));
+#endif // _DEBUG
+#endif // !HOST_UNIX
+
+#if !defined (STRESS_LOG_READONLY) && defined(HOST_WINDOWS)
+ StressLogChunk::s_LogChunkHeap = HeapCreate (0, STRESSLOG_CHUNK_SIZE * 128, 0);
+ if (StressLogChunk::s_LogChunkHeap == NULL)
+ {
+ StressLogChunk::s_LogChunkHeap = GetProcessHeap ();
+ }
+ _ASSERTE (StressLogChunk::s_LogChunkHeap);
+#endif //!STRESS_LOG_READONLY
+}
+
+/*********************************************************************************/
+void StressLog::Terminate(BOOL fProcessDetach) {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ theLog.facilitiesToLog = 0;
+
+ StressLogLockHolder lockh(theLog.lock, FALSE);
+ if (!fProcessDetach) {
+ lockh.Acquire(); lockh.Release(); // The Enter() Leave() forces a memory barrier on weak memory model systems
+ // we want all the other threads to notice that facilitiesToLog is now zero
+
+ // This is not strictly threadsafe, since there is no way of insuring when all the
+ // threads are out of logMsg. In practice, since they can no longer enter logMsg
+ // and there are no blocking operations in logMsg, simply sleeping will insure
+ // that everyone gets out.
+ ClrSleepEx(2, FALSE);
+ lockh.Acquire();
+ }
+
+ // Free the log memory
+ ThreadStressLog* ptr = theLog.logs;
+ theLog.logs = 0;
+ while(ptr != 0) {
+ ThreadStressLog* tmp = ptr;
+ ptr = ptr->next;
+ delete tmp;
+ }
+
+ if (!fProcessDetach) {
+ lockh.Release();
+ }
+
+#if !defined (STRESS_LOG_READONLY) && defined(HOST_WINDOWS)
+ if (StressLogChunk::s_LogChunkHeap != NULL && StressLogChunk::s_LogChunkHeap != GetProcessHeap ())
+ {
+ HeapDestroy (StressLogChunk::s_LogChunkHeap);
+ }
+#endif //!STRESS_LOG_READONLY
+}
+
+/*********************************************************************************/
+/* create a new thread stress log buffer associated with Thread local slot, for the Stress log */
+
+ThreadStressLog* StressLog::CreateThreadStressLog() {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ }
+ CONTRACTL_END;
+
+ static PVOID callerID = NULL;
+
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
+ if (msgs != NULL)
+ {
+ return msgs;
+ }
+
+ if (callerID == ClrTeb::GetFiberPtrId())
+ {
+ return NULL;
+ }
+
+#ifdef HOST_WINDOWS
+ if (!StressLogChunk::s_LogChunkHeap)
+ {
+ return NULL;
+ }
+#endif
+
+ //if we are not allowed to allocate stress log, we should not even try to take the lock
+ if (IsInCantAllocStressLogRegion ())
+ {
+ return NULL;
+ }
+
+ // if it looks like we won't be allowed to allocate a new chunk, exit early
+ if (theLog.deadCount == 0 && !AllowNewChunk (0))
+ {
+ return NULL;
+ }
+
+ StressLogLockHolder lockh(theLog.lock, FALSE);
+
+ class NestedCaller
+ {
+ public:
+ NestedCaller()
+ {
+ }
+ ~NestedCaller()
+ {
+ callerID = NULL;
+ }
+ void Mark()
+ {
+ callerID = ClrTeb::GetFiberPtrId();
+ }
+ };
+
+ NestedCaller nested;
+
+ BOOL noFLSNow = FALSE;
+
+ PAL_CPP_TRY
+ {
+ // Acquiring the lack can throw an OOM exception the first time its called on a thread. We go
+ // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if
+ // we fail.
+ lockh.Acquire();
+ nested.Mark();
+
+ // ClrFlsSetValue can throw an OOM exception the first time its called on a thread for a given slot. We go
+ // ahead and try to provoke that now, before we've altered the list of available stress logs, and bail if
+ // we fail.
+ t_pCurrentThreadLog = NULL;
+ }
+#pragma warning(suppress: 4101)
+ PAL_CPP_CATCH_DERIVED(OutOfMemoryException, obj)
+ {
+ // Just leave on any exception. Note: can't goto or return from within EX_CATCH...
+ noFLSNow = TRUE;
+ }
+ PAL_CPP_ENDTRY;
+
+ if (noFLSNow == FALSE && theLog.facilitiesToLog != 0)
+ msgs = CreateThreadStressLogHelper();
+
+ return msgs;
+}
+
+ThreadStressLog* StressLog::CreateThreadStressLogHelper() {
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ BOOL skipInsert = FALSE;
+ ThreadStressLog* msgs = NULL;
+
+ // See if we can recycle a dead thread
+ if (theLog.deadCount > 0)
+ {
+ unsigned __int64 recycleStamp = getTimeStamp() - RECYCLE_AGE;
+ msgs = theLog.logs;
+ //find out oldest dead ThreadStressLog in case we can't find one within
+ //recycle age but can't create a new chunk
+ ThreadStressLog * oldestDeadMsg = NULL;
+
+ while(msgs != 0)
+ {
+ if (msgs->isDead)
+ {
+ BOOL hasTimeStamp = msgs->curPtr != (StressMsg *)msgs->chunkListTail->EndPtr();
+ if (hasTimeStamp && msgs->curPtr->timeStamp < recycleStamp)
+ {
+ skipInsert = TRUE;
+ InterlockedDecrement(&theLog.deadCount);
+ break;
+ }
+
+ if (!oldestDeadMsg)
+ {
+ oldestDeadMsg = msgs;
+ }
+ else if (hasTimeStamp && oldestDeadMsg->curPtr->timeStamp > msgs->curPtr->timeStamp)
+ {
+ oldestDeadMsg = msgs;
+ }
+ }
+
+ msgs = msgs->next;
+ }
+
+ //if the total stress log size limit is already passed and we can't add new chunk,
+ //always reuse the oldest dead msg
+ if (!AllowNewChunk (0) && !msgs)
+ {
+ msgs = oldestDeadMsg;
+ skipInsert = TRUE;
+ InterlockedDecrement(&theLog.deadCount);
+ }
+ }
+
+ if (msgs == 0) {
+ FAULT_NOT_FATAL(); // We don't mind if we can't allocate here, we'll try again later.
+ if (IsInCantAllocStressLogRegion ())
+ {
+ goto LEAVE;
+ }
+
+ msgs = new (nothrow) ThreadStressLog;
+
+ if (msgs == 0 ||!msgs->IsValid ())
+ {
+ delete msgs;
+ msgs = 0;
+ goto LEAVE;
+ }
+ }
+
+ msgs->Activate ();
+
+ t_pCurrentThreadLog = msgs;
+
+ if (!skipInsert) {
+#ifdef _DEBUG
+ ThreadStressLog* walk = theLog.logs;
+ while (walk)
+ {
+ _ASSERTE (walk != msgs);
+ walk = walk->next;
+ }
+#endif
+ // Put it into the stress log
+ msgs->next = theLog.logs;
+ theLog.logs = msgs;
+ }
+
+LEAVE:
+ ;
+ return msgs;
+}
+
+/*********************************************************************************/
+/* static */
+void StressLog::ThreadDetach() {
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
+
+#ifndef DACCESS_COMPILE
+ if (msgs == 0)
+ {
+ return;
+ }
+
+ t_pCurrentThreadLog = NULL;
+
+ // We are deleting a fiber. The thread is running a different fiber now.
+ // We should write this message to the StressLog for deleted fiber.
+ msgs->LogMsg (LF_STARTUP, 0, "******* DllMain THREAD_DETACH called Thread dying *******\n");
+#endif
+
+ msgs->isDead = TRUE;
+ InterlockedIncrement(&theLog.deadCount);
+}
+
+BOOL StressLog::AllowNewChunk (LONG numChunksInCurThread)
+{
+ _ASSERTE (numChunksInCurThread <= theLog.totalChunk);
+ DWORD perThreadLimit = theLog.MaxSizePerThread;
+
+#ifndef DACCESS_COMPILE
+ if (numChunksInCurThread == 0 && IsSuspendEEThread())
+ return TRUE;
+
+ if (IsGCSpecialThread())
+ {
+ perThreadLimit *= GC_STRESSLOG_MULTIPLY;
+ }
+#endif
+
+ if ((DWORD)numChunksInCurThread * STRESSLOG_CHUNK_SIZE >= perThreadLimit)
+ {
+ return FALSE;
+ }
+
+ return (DWORD)theLog.totalChunk * STRESSLOG_CHUNK_SIZE < theLog.MaxSizeTotal;
+}
+
+BOOL StressLog::ReserveStressLogChunks (unsigned chunksToReserve)
+{
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
+
+ if (msgs == 0)
+ {
+ msgs = CreateThreadStressLog();
+
+ if (msgs == 0)
+ return FALSE;
+ }
+
+ if (chunksToReserve == 0)
+ {
+ chunksToReserve = (theLog.MaxSizePerThread + STRESSLOG_CHUNK_SIZE - 1) / STRESSLOG_CHUNK_SIZE;
+ }
+
+ LONG numTries = (LONG)chunksToReserve - msgs->chunkListLength;
+ for (LONG i = 0; i < numTries; i++)
+ {
+ msgs->GrowChunkList ();
+ }
+
+ return msgs->chunkListLength >= (LONG)chunksToReserve;
+}
+
+void (*FSwitchToSOTolerant)();
+void (*FSwitchToSOIntolerant)();
+void TrackSO(BOOL tolerance)
+{
+ if (tolerance)
+ {
+ if (FSwitchToSOTolerant)
+ {
+ FSwitchToSOTolerant();
+ }
+ }
+ else
+ {
+ if (FSwitchToSOIntolerant)
+ {
+ FSwitchToSOIntolerant();
+ }
+ }
+}
+
+/*********************************************************************************/
+/* fetch a buffer that can be used to write a stress message, it is thread safe */
+void ThreadStressLog::LogMsg(unsigned facility, int cArgs, const char* format, va_list Args)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ // Asserts in this function cause infinite loops in the asserting mechanism.
+ // Just use debug breaks instead.
+
+#ifndef DACCESS_COMPILE
+#ifdef _DEBUG
+ // _ASSERTE ( cArgs >= 0 && cArgs <= 7 );
+ if (cArgs < 0 || cArgs > 7) DebugBreak();
+#endif //
+
+ size_t offs = ((size_t)format - StressLog::theLog.moduleOffset);
+
+ // _ASSERTE ( offs < StressMsg::maxOffset );
+ if (offs >= StressMsg::maxOffset)
+ {
+#ifdef _DEBUG
+ DebugBreak(); // in lieu of the above _ASSERTE
+#endif // _DEBUG
+
+ // Set it to this string instead.
+ offs =
+#ifdef _DEBUG
+ (size_t)"<BUG: StressLog format string beyond maxOffset>";
+#else // _DEBUG
+ 0; // a 0 offset is ignored by StressLog::Dump
+#endif // _DEBUG else
+ }
+
+ // Get next available slot
+ StressMsg* msg = AdvanceWrite(cArgs);
+
+ msg->timeStamp = getTimeStamp();
+ msg->facility = facility;
+ msg->formatOffset = offs;
+ msg->numberOfArgs = cArgs;
+
+ for ( int i = 0; i < cArgs; ++i )
+ {
+ void* data = va_arg(Args, void*);
+ msg->args[i] = data;
+ }
+
+#ifdef _DEBUG
+ if (!IsValid () || threadId != GetCurrentThreadId ())
+ DebugBreak();
+#endif // _DEBUG
+#endif //DACCESS_COMPILE
+}
+
+FORCEINLINE BOOL StressLog::InlinedStressLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+#if defined(DACCESS_COMPILE)
+ return FALSE;
+#else
+ return ((theLog.facilitiesToLog & facility) && (level <= theLog.levelToLog));
+#endif
+}
+
+BOOL StressLog::StressLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedStressLogOn(facility, level);
+}
+
+FORCEINLINE BOOL StressLog::InlinedETWLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return FALSE;
+}
+
+BOOL StressLog::ETWLogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedETWLogOn(facility, level);
+}
+
+#if !defined(DACCESS_COMPILE)
+BOOL StressLog::LogOn(unsigned facility, unsigned level)
+{
+ STATIC_CONTRACT_LEAF;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ return InlinedStressLogOn(facility, level) || InlinedETWLogOn(facility, level);
+}
+#endif
+
+/* static */
+void StressLog::LogMsg (unsigned level, unsigned facility, int cArgs, const char* format, ... )
+{
+ STATIC_CONTRACT_SUPPORTS_DAC;
+#ifndef DACCESS_COMPILE
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_SUPPORTS_DAC;
+
+ // Any stresslog LogMsg could theoretically create a new stress log and thus
+ // enter a critical section. But we don't want these to cause violations in
+ // CANNOT_TAKE_LOCK callers, since the callers would otherwise be fine in runs that don't
+ // set the stress log config parameter.
+ CONTRACT_VIOLATION(TakesLockViolation);
+
+ _ASSERTE ( cArgs >= 0 && cArgs <= 7 );
+
+ va_list Args;
+
+ if(InlinedStressLogOn(facility, level))
+ {
+ ThreadStressLog* msgs = t_pCurrentThreadLog;
+
+ if (msgs == 0) {
+ msgs = CreateThreadStressLog();
+
+ if (msgs == 0)
+ return;
+ }
+ va_start(Args, format);
+ msgs->LogMsg (facility, cArgs, format, Args);
+ va_end(Args);
+ }
+
+// Stress Log ETW feature available only on the desktop versions of the runtime
+#endif //!DACCESS_COMPILE
+}
+
+#ifdef _DEBUG
+/* static */
+void StressLog::LogCallStack(const char *const callTag){
+ if (theLog.RtlCaptureStackBackTrace)
+ {
+ size_t CallStackTrace[MAX_CALL_STACK_TRACE];
+ ULONG hash;
+ USHORT stackTraceCount = theLog.RtlCaptureStackBackTrace (2, MAX_CALL_STACK_TRACE, (PVOID *)CallStackTrace, &hash);
+ if (stackTraceCount > MAX_CALL_STACK_TRACE)
+ stackTraceCount = MAX_CALL_STACK_TRACE;
+ LogMsgOL("Start of %s stack \n", callTag);
+ USHORT i = 0;
+ for (;i < stackTraceCount; i++)
+ {
+ LogMsgOL("(%s stack)%pK\n", callTag, CallStackTrace[i]);
+ }
+ LogMsgOL("End of %s stack\n", callTag);
+ }
+}
+#endif //_DEBUG
+
+#endif // STRESS_LOG
+
diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp
new file mode 100644
index 00000000000..c034ae19318
--- /dev/null
+++ b/src/coreclr/utilcode/util.cpp
@@ -0,0 +1,3292 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// util.cpp
+//
+
+//
+// This contains a bunch of C++ utility classes.
+//
+//*****************************************************************************
+#include "stdafx.h" // Precompiled header key.
+#include "utilcode.h"
+#include "metadata.h"
+#include "ex.h"
+#include "pedecoder.h"
+#include "loaderheap.h"
+#include "sigparser.h"
+#include "cor.h"
+#include "corinfo.h"
+#include "volatile.h"
+#include "mdfileformat.h"
+
+#ifndef DACCESS_COMPILE
+UINT32 g_nClrInstanceId = 0;
+#endif //!DACCESS_COMPILE
+
+//********** Code. ************************************************************
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
+extern WinRTStatusEnum gWinRTStatus = WINRT_STATUS_UNINITED;
+#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
+
+#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORESYSTEM)
+//------------------------------------------------------------------------------
+//
+// Attempt to detect the presense of Windows Runtime support on the current OS.
+// Our algorithm to do this is to ensure that:
+// 1. combase.dll exists
+// 2. combase.dll contains a RoInitialize export
+//
+
+void InitWinRTStatus()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ WinRTStatusEnum winRTStatus = WINRT_STATUS_UNSUPPORTED;
+
+ const WCHAR wszComBaseDll[] = W("\\combase.dll");
+ const SIZE_T cchComBaseDll = _countof(wszComBaseDll);
+
+ WCHAR wszComBasePath[MAX_LONGPATH + 1];
+ const SIZE_T cchComBasePath = _countof(wszComBasePath);
+
+ ZeroMemory(wszComBasePath, cchComBasePath * sizeof(wszComBasePath[0]));
+
+ UINT cchSystemDirectory = WszGetSystemDirectory(wszComBasePath, MAX_LONGPATH);
+
+ // Make sure that we're only probing in the system directory. If we can't find the system directory, or
+ // we find it but combase.dll doesn't fit into it, we'll fall back to a safe default of saying that WinRT
+ // is simply not present.
+ if (cchSystemDirectory > 0 && cchComBasePath - cchSystemDirectory >= cchComBaseDll)
+ {
+ if (wcscat_s(wszComBasePath, wszComBaseDll) == 0)
+ {
+ HModuleHolder hComBase(WszLoadLibrary(wszComBasePath));
+ if (hComBase != NULL)
+ {
+ FARPROC activateInstace = GetProcAddress(hComBase, "RoInitialize");
+ if (activateInstace != NULL)
+ {
+ winRTStatus = WINRT_STATUS_SUPPORTED;
+ }
+ }
+ }
+ }
+
+ gWinRTStatus = winRTStatus;
+}
+#endif // FEATURE_COMINTEROP && !FEATURE_CORESYSTEM
+//*****************************************************************************
+// Convert a string of hex digits into a hex value of the specified # of bytes.
+//*****************************************************************************
+HRESULT GetHex( // Return status.
+ LPCSTR szStr, // String to convert.
+ int size, // # of bytes in pResult.
+ void *pResult) // Buffer for result.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int count = size * 2; // # of bytes to take from string.
+ unsigned int Result = 0; // Result value.
+ char ch;
+
+ _ASSERTE(size == 1 || size == 2 || size == 4);
+
+ while (count-- && (ch = *szStr++) != '\0')
+ {
+ switch (ch)
+ {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ Result = 16 * Result + (ch - '0');
+ break;
+
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ Result = 16 * Result + 10 + (ch - 'A');
+ break;
+
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ Result = 16 * Result + 10 + (ch - 'a');
+ break;
+
+ default:
+ return (E_FAIL);
+ }
+ }
+
+ // Set the output.
+ switch (size)
+ {
+ case 1:
+ *((BYTE *) pResult) = (BYTE) Result;
+ break;
+
+ case 2:
+ *((WORD *) pResult) = (WORD) Result;
+ break;
+
+ case 4:
+ *((DWORD *) pResult) = Result;
+ break;
+
+ default:
+ _ASSERTE(0);
+ break;
+ }
+ return (S_OK);
+}
+
+//*****************************************************************************
+// Convert a pointer to a string into a GUID.
+//*****************************************************************************
+HRESULT LPCSTRToGuid( // Return status.
+ LPCSTR szGuid, // String to convert.
+ GUID *psGuid) // Buffer for converted GUID.
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ // Verify the surrounding syntax.
+ if (strlen(szGuid) != 38 || szGuid[0] != '{' || szGuid[9] != '-' ||
+ szGuid[14] != '-' || szGuid[19] != '-' || szGuid[24] != '-' || szGuid[37] != '}')
+ {
+ return (E_FAIL);
+ }
+
+ // Parse the first 3 fields.
+ if (FAILED(GetHex(szGuid + 1, 4, &psGuid->Data1)))
+ return E_FAIL;
+ if (FAILED(GetHex(szGuid + 10, 2, &psGuid->Data2)))
+ return E_FAIL;
+ if (FAILED(GetHex(szGuid + 15, 2, &psGuid->Data3)))
+ return E_FAIL;
+
+ // Get the last two fields (which are byte arrays).
+ for (i = 0; i < 2; ++i)
+ {
+ if (FAILED(GetHex(szGuid + 20 + (i * 2), 1, &psGuid->Data4[i])))
+ {
+ return E_FAIL;
+ }
+ }
+ for (i=0; i < 6; ++i)
+ {
+ if (FAILED(GetHex(szGuid + 25 + (i * 2), 1, &psGuid->Data4[i+2])))
+ {
+ return E_FAIL;
+ }
+ }
+ return S_OK;
+}
+
+//
+//
+// Global utility functions.
+//
+//
+
+
+
+typedef HRESULT __stdcall DLLGETCLASSOBJECT(REFCLSID rclsid,
+ REFIID riid,
+ void **ppv);
+
+EXTERN_C const IID _IID_IClassFactory =
+ {0x00000001, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
+
+namespace
+{
+ HRESULT FakeCoCallDllGetClassObject(
+ REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void **ppv,
+ HMODULE *phmodDll)
+ {
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(ppv != nullptr);
+
+ HRESULT hr = S_OK;
+
+ // Initialize [out] HMODULE (if it was requested)
+ if (phmodDll != nullptr)
+ *phmodDll = nullptr;
+
+ bool fIsDllPathPrefix = (wszDllPath != nullptr) && (wcslen(wszDllPath) > 0) && (wszDllPath[wcslen(wszDllPath) - 1] == W('\\'));
+
+ // - An empty string will be treated as NULL.
+ // - A string ending will a backslash will be treated as a prefix for where to look for the DLL
+ // if the InProcServer32 value is just a DLL name and not a full path.
+ StackSString ssDllName;
+ if ((wszDllPath == nullptr) || (wszDllPath[0] == W('\0')) || fIsDllPathPrefix)
+ {
+#ifndef TARGET_UNIX
+ IfFailRet(Clr::Util::Com::FindInprocServer32UsingCLSID(rclsid, ssDllName));
+
+ EX_TRY
+ {
+ if (fIsDllPathPrefix)
+ {
+ SString::Iterator i = ssDllName.Begin();
+ if (!ssDllName.Find(i, W('\\')))
+ { // If the InprocServer32 is just a DLL name (not a fully qualified path), then
+ // prefix wszFilePath with wszDllPath.
+ ssDllName.Insert(i, wszDllPath);
+ }
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ IfFailRet(hr);
+
+ wszDllPath = ssDllName.GetUnicode();
+#else // !TARGET_UNIX
+ return E_FAIL;
+#endif // !TARGET_UNIX
+ }
+ _ASSERTE(wszDllPath != nullptr);
+
+ // We've got the name of the DLL to load, so load it.
+ HModuleHolder hDll = WszLoadLibraryEx(wszDllPath, nullptr, GetLoadWithAlteredSearchPathFlag());
+ if (hDll == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ // We've loaded the DLL, so find the DllGetClassObject function.
+ DLLGETCLASSOBJECT *dllGetClassObject = (DLLGETCLASSOBJECT*)GetProcAddress(hDll, "DllGetClassObject");
+ if (dllGetClassObject == nullptr)
+ return HRESULT_FROM_GetLastError();
+
+ // Call the function to get a class object for the rclsid and riid passed in.
+ IfFailRet(dllGetClassObject(rclsid, riid, ppv));
+
+ hDll.SuppressRelease();
+
+ if (phmodDll != nullptr)
+ *phmodDll = hDll.GetValue();
+
+ return hr;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// FakeCoCreateInstanceEx
+//
+// Description:
+// A private function to do the equivalent of a CoCreateInstance in cases where we
+// can't make the real call. Use this when, for instance, you need to create a symbol
+// reader in the Runtime but we're not CoInitialized. Obviously, this is only good
+// for COM objects for which CoCreateInstance is just a glorified find-and-load-me
+// operation.
+//
+// Arguments:
+// * rclsid - [in] CLSID of object to instantiate
+// * wszDllPath [in] - Path to profiler DLL. If wszDllPath is NULL, FakeCoCreateInstanceEx
+// will look up the registry to find the path of the COM dll associated with rclsid.
+// If the path ends in a backslash, FakeCoCreateInstanceEx will treat this as a prefix
+// if the InprocServer32 found in the registry is a simple filename (not a full path).
+// This allows the caller to specify the directory in which the InprocServer32 should
+// be found.
+// * riid - [in] IID of interface on object to return in ppv
+// * ppv - [out] Pointer to implementation of requested interface
+// * phmodDll - [out] HMODULE of DLL that was loaded to instantiate the COM object.
+// The caller may eventually call FreeLibrary() on this if it can be determined
+// that we no longer reference the generated COM object or dependencies. Else, the
+// caller may ignore this and the DLL will stay loaded forever. If caller
+// specifies phmodDll==NULL, then this parameter is ignored and the HMODULE is not
+// returned.
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * (*phmodDll) on [out] may always be trusted, even if this function returns an
+// error. Therefore, even if creation of the COM object failed, if (*phmodDll !=
+// NULL), then the DLL was actually loaded. The caller may wish to call
+// FreeLibrary on (*phmodDll) in such a case.
+HRESULT FakeCoCreateInstanceEx(REFCLSID rclsid,
+ LPCWSTR wszDllPath,
+ REFIID riid,
+ void ** ppv,
+ HMODULE * phmodDll)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+
+ // Call the function to get a class factory for the rclsid passed in.
+ HModuleHolder hDll;
+ ReleaseHolder<IClassFactory> classFactory;
+ IfFailRet(FakeCoCallDllGetClassObject(rclsid, wszDllPath, _IID_IClassFactory, (void**)&classFactory, &hDll));
+
+ // Ask the class factory to create an instance of the
+ // necessary object.
+ IfFailRet(classFactory->CreateInstance(NULL, riid, ppv));
+
+ hDll.SuppressRelease();
+
+ if (phmodDll != NULL)
+ {
+ *phmodDll = hDll.GetValue();
+ }
+
+ return hr;
+}
+
+#if USE_UPPER_ADDRESS
+static BYTE * s_CodeMinAddr; // Preferred region to allocate the code in.
+static BYTE * s_CodeMaxAddr;
+static BYTE * s_CodeAllocStart;
+static BYTE * s_CodeAllocHint; // Next address to try to allocate for code in the preferred region.
+#endif
+
+//
+// Use this function to initialize the s_CodeAllocHint
+// during startup. base is runtime .dll base address,
+// size is runtime .dll virtual size.
+//
+void InitCodeAllocHint(SIZE_T base, SIZE_T size, int randomPageOffset)
+{
+#if USE_UPPER_ADDRESS
+
+#ifdef _DEBUG
+ // If GetForceRelocs is enabled we don't constrain the pMinAddr
+ if (PEDecoder::GetForceRelocs())
+ return;
+#endif
+
+//
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any code heap that doesn't specify an address
+ // range using [pMinAddr..pMaxAddr] we place it in the
+ // upper address space
+ // This enables us to avoid having to use long JumpStubs
+ // to reach the code for our ngen-ed images.
+ // Which are also placed in the UPPER_ADDRESS space.
+ //
+ SIZE_T reach = 0x7FFF0000u;
+
+ // We will choose the preferred code region based on the address of clr.dll. The JIT helpers
+ // in clr.dll are the most heavily called functions.
+ s_CodeMinAddr = (base + size > reach) ? (BYTE *)(base + size - reach) : (BYTE *)0;
+ s_CodeMaxAddr = (base + reach > base) ? (BYTE *)(base + reach) : (BYTE *)-1;
+
+ BYTE * pStart;
+
+ if (s_CodeMinAddr <= (BYTE *)CODEHEAP_START_ADDRESS &&
+ (BYTE *)CODEHEAP_START_ADDRESS < s_CodeMaxAddr)
+ {
+ // clr.dll got loaded at its preferred base address? (OS without ASLR - pre-Vista)
+ // Use the code head start address that does not cause collisions with NGen images.
+ // This logic is coupled with scripts that we use to assign base addresses.
+ pStart = (BYTE *)CODEHEAP_START_ADDRESS;
+ }
+ else
+ if (base > UINT32_MAX)
+ {
+ // clr.dll got address assigned by ASLR?
+ // Try to occupy the space as far as possible to minimize collisions with other ASLR assigned
+ // addresses. Do not start at s_CodeMinAddr exactly so that we can also reach common native images
+ // that can be placed at higher addresses than clr.dll.
+ pStart = s_CodeMinAddr + (s_CodeMaxAddr - s_CodeMinAddr) / 8;
+ }
+ else
+ {
+ // clr.dll missed the base address?
+ // Try to occupy the space right after it.
+ pStart = (BYTE *)(base + size);
+ }
+
+ // Randomize the address space
+ pStart += GetOsPageSize() * randomPageOffset;
+
+ s_CodeAllocStart = pStart;
+ s_CodeAllocHint = pStart;
+#endif
+}
+
+//
+// Use this function to reset the s_CodeAllocHint
+// after unloading an AppDomain
+//
+void ResetCodeAllocHint()
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ s_CodeAllocHint = s_CodeAllocStart;
+#endif
+}
+
+//
+// Returns TRUE if p is located in near clr.dll that allows us
+// to use rel32 IP-relative addressing modes.
+//
+BOOL IsPreferredExecutableRange(void * p)
+{
+ LIMITED_METHOD_CONTRACT;
+#if USE_UPPER_ADDRESS
+ if (s_CodeMinAddr <= (BYTE *)p && (BYTE *)p < s_CodeMaxAddr)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+//
+// Allocate free memory that will be used for executable code
+// Handles the special requirements that we have on 64-bit platforms
+// where we want the executable memory to be located near clr.dll
+//
+BYTE * ClrVirtualAllocExecutable(SIZE_T dwSize,
+ DWORD flAllocationType,
+ DWORD flProtect)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+#if USE_UPPER_ADDRESS
+ //
+ // If we are using the UPPER_ADDRESS space (on Win64)
+ // then for any heap that will contain executable code
+ // we will place it in the upper address space
+ //
+ // This enables us to avoid having to use JumpStubs
+ // to reach the code for our ngen-ed images on x64,
+ // since they are also placed in the UPPER_ADDRESS space.
+ //
+ BYTE * pHint = s_CodeAllocHint;
+
+ if (dwSize <= (SIZE_T)(s_CodeMaxAddr - s_CodeMinAddr) && pHint != NULL)
+ {
+ // Try to allocate in the preferred region after the hint
+ BYTE * pResult = ClrVirtualAllocWithinRange(pHint, s_CodeMaxAddr, dwSize, flAllocationType, flProtect);
+
+ if (pResult != NULL)
+ {
+ s_CodeAllocHint = pResult + dwSize;
+ return pResult;
+ }
+
+ // Try to allocate in the preferred region before the hint
+ pResult = ClrVirtualAllocWithinRange(s_CodeMinAddr, pHint + dwSize, dwSize, flAllocationType, flProtect);
+
+ if (pResult != NULL)
+ {
+ s_CodeAllocHint = pResult + dwSize;
+ return pResult;
+ }
+
+ s_CodeAllocHint = NULL;
+ }
+
+ // Fall through to
+#endif // USE_UPPER_ADDRESS
+
+#ifdef HOST_UNIX
+ // Tell PAL to use the executable memory allocator to satisfy this request for virtual memory.
+ // This will allow us to place JIT'ed code close to the coreclr library
+ // and thus improve performance by avoiding jump stubs in managed code.
+ flAllocationType |= MEM_RESERVE_EXECUTABLE;
+#endif // HOST_UNIX
+
+ return (BYTE *) ClrVirtualAlloc (NULL, dwSize, flAllocationType, flProtect);
+
+}
+
+//
+// Allocate free memory with specific alignment.
+//
+LPVOID ClrVirtualAllocAligned(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect, SIZE_T alignment)
+{
+ // Verify that the alignment is a power of 2
+ _ASSERTE(alignment != 0);
+ _ASSERTE((alignment & (alignment - 1)) == 0);
+
+#ifdef HOST_WINDOWS
+
+ // The VirtualAlloc on Windows ensures 64kB alignment
+ _ASSERTE(alignment <= 0x10000);
+ return ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+
+#else // HOST_WINDOWS
+
+ if(alignment < GetOsPageSize()) alignment = GetOsPageSize();
+
+ // UNIXTODO: Add a specialized function to PAL so that we don't have to waste memory
+ dwSize += alignment;
+ SIZE_T addr = (SIZE_T)ClrVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
+ return (LPVOID)((addr + (alignment - 1)) & ~(alignment - 1));
+
+#endif // HOST_WINDOWS
+}
+
+#ifdef _DEBUG
+static DWORD ShouldInjectFaultInRange()
+{
+ static DWORD fInjectFaultInRange = 99;
+
+ if (fInjectFaultInRange == 99)
+ fInjectFaultInRange = (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_InjectFault) & 0x40);
+ return fInjectFaultInRange;
+}
+#endif
+
+// Reserves free memory within the range [pMinAddr..pMaxAddr] using
+// ClrVirtualQuery to find free memory and ClrVirtualAlloc to reserve it.
+//
+// This method only supports the flAllocationType of MEM_RESERVE, and expects that the memory
+// is being reserved for the purpose of eventually storing executable code.
+//
+// Callers also should set dwSize to a multiple of sysInfo.dwAllocationGranularity (64k).
+// That way they can reserve a large region and commit smaller sized pages
+// from that region until it fills up.
+//
+// This functions returns the reserved memory block upon success
+//
+// It returns NULL when it fails to find any memory that satisfies
+// the range.
+//
+
+BYTE * ClrVirtualAllocWithinRange(const BYTE *pMinAddr,
+ const BYTE *pMaxAddr,
+ SIZE_T dwSize,
+ DWORD flAllocationType,
+ DWORD flProtect)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ PRECONDITION(dwSize != 0);
+ PRECONDITION(flAllocationType == MEM_RESERVE);
+ }
+ CONTRACTL_END;
+
+ BYTE *pResult = nullptr; // our return value;
+
+ static unsigned countOfCalls = 0; // We log the number of tims we call this method
+ countOfCalls++; // increment the call counter
+
+ if (dwSize == 0)
+ {
+ return nullptr;
+ }
+
+ //
+ // First lets normalize the pMinAddr and pMaxAddr values
+ //
+ // If pMinAddr is NULL then set it to BOT_MEMORY
+ if ((pMinAddr == 0) || (pMinAddr < (BYTE *) BOT_MEMORY))
+ {
+ pMinAddr = (BYTE *) BOT_MEMORY;
+ }
+
+ // If pMaxAddr is NULL then set it to TOP_MEMORY
+ if ((pMaxAddr == 0) || (pMaxAddr > (BYTE *) TOP_MEMORY))
+ {
+ pMaxAddr = (BYTE *) TOP_MEMORY;
+ }
+
+ // If pMaxAddr is not greater than pMinAddr we can not make an allocation
+ if (pMaxAddr <= pMinAddr)
+ {
+ return nullptr;
+ }
+
+ // If pMinAddr is BOT_MEMORY and pMaxAddr is TOP_MEMORY
+ // then we can call ClrVirtualAlloc instead
+ if ((pMinAddr == (BYTE *) BOT_MEMORY) && (pMaxAddr == (BYTE *) TOP_MEMORY))
+ {
+ return (BYTE*) ClrVirtualAlloc(nullptr, dwSize, flAllocationType, flProtect);
+ }
+
+#ifdef HOST_UNIX
+ pResult = (BYTE *)PAL_VirtualReserveFromExecutableMemoryAllocatorWithinRange(pMinAddr, pMaxAddr, dwSize);
+ if (pResult != nullptr)
+ {
+ return pResult;
+ }
+#endif // HOST_UNIX
+
+ // We will do one scan from [pMinAddr .. pMaxAddr]
+ // First align the tryAddr up to next 64k base address.
+ // See docs for VirtualAllocEx and lpAddress and 64k alignment for reasons.
+ //
+ BYTE * tryAddr = (BYTE *)ALIGN_UP((BYTE *)pMinAddr, VIRTUAL_ALLOC_RESERVE_GRANULARITY);
+ bool virtualQueryFailed = false;
+ bool faultInjected = false;
+ unsigned virtualQueryCount = 0;
+
+ // Now scan memory and try to find a free block of the size requested.
+ while ((tryAddr + dwSize) <= (BYTE *) pMaxAddr)
+ {
+ MEMORY_BASIC_INFORMATION mbInfo;
+
+ // Use VirtualQuery to find out if this address is MEM_FREE
+ //
+ virtualQueryCount++;
+ if (!ClrVirtualQuery((LPCVOID)tryAddr, &mbInfo, sizeof(mbInfo)))
+ {
+ // Exit and return nullptr if the VirtualQuery call fails.
+ virtualQueryFailed = true;
+ break;
+ }
+
+ // Is there enough memory free from this start location?
+ // Note that for most versions of UNIX the mbInfo.RegionSize returned will always be 0
+ if ((mbInfo.State == MEM_FREE) &&
+ (mbInfo.RegionSize >= (SIZE_T) dwSize || mbInfo.RegionSize == 0))
+ {
+ // Try reserving the memory using VirtualAlloc now
+ pResult = (BYTE*)ClrVirtualAlloc(tryAddr, dwSize, MEM_RESERVE, flProtect);
+
+ // Normally this will be successful
+ //
+ if (pResult != nullptr)
+ {
+ // return pResult
+ break;
+ }
+
+#ifdef _DEBUG
+ if (ShouldInjectFaultInRange())
+ {
+ // return nullptr (failure)
+ faultInjected = true;
+ break;
+ }
+#endif // _DEBUG
+
+ // On UNIX we can also fail if our request size 'dwSize' is larger than 64K and
+ // and our tryAddr is pointing at a small MEM_FREE region (smaller than 'dwSize')
+ // However we can't distinguish between this and the race case.
+
+ // We might fail in a race. So just move on to next region and continue trying
+ tryAddr = tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY;
+ }
+ else
+ {
+ // Try another section of memory
+ tryAddr = max(tryAddr + VIRTUAL_ALLOC_RESERVE_GRANULARITY,
+ (BYTE*) mbInfo.BaseAddress + mbInfo.RegionSize);
+ }
+ }
+
+ STRESS_LOG7(LF_JIT, LL_INFO100,
+ "ClrVirtualAllocWithinRange request #%u for %08x bytes in [ %p .. %p ], query count was %u - returned %s: %p\n",
+ countOfCalls, (DWORD)dwSize, pMinAddr, pMaxAddr,
+ virtualQueryCount, (pResult != nullptr) ? "success" : "failure", pResult);
+
+ // If we failed this call the process will typically be terminated
+ // so we log any additional reason for failing this call.
+ //
+ if (pResult == nullptr)
+ {
+ if ((tryAddr + dwSize) > (BYTE *)pMaxAddr)
+ {
+ // Our tryAddr reached pMaxAddr
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: Address space exhausted.\n");
+ }
+
+ if (virtualQueryFailed)
+ {
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: VirtualQuery operation failed.\n");
+ }
+
+ if (faultInjected)
+ {
+ STRESS_LOG0(LF_JIT, LL_INFO100, "Additional reason: fault injected.\n");
+ }
+ }
+
+ return pResult;
+}
+
+//******************************************************************************
+// NumaNodeInfo
+//******************************************************************************
+#if !defined(FEATURE_REDHAWK)
+
+/*static*/ LPVOID NumaNodeInfo::VirtualAllocExNuma(HANDLE hProc, LPVOID lpAddr, SIZE_T dwSize,
+ DWORD allocType, DWORD prot, DWORD node)
+{
+ return ::VirtualAllocExNuma(hProc, lpAddr, dwSize, allocType, prot, node);
+}
+
+#ifdef HOST_WINDOWS
+/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(PPROCESSOR_NUMBER proc_no, PUSHORT node_no)
+{
+ return ::GetNumaProcessorNodeEx(proc_no, node_no);
+}
+/*static*/ bool NumaNodeInfo::GetNumaInfo(PUSHORT total_nodes, DWORD* max_procs_per_node)
+{
+ if (m_enableGCNumaAware)
+ {
+ DWORD currentProcsOnNode = 0;
+ for (int i = 0; i < m_nNodes; i++)
+ {
+ GROUP_AFFINITY processorMask;
+ if (GetNumaNodeProcessorMaskEx(i, &processorMask))
+ {
+ DWORD procsOnNode = 0;
+ uintptr_t mask = (uintptr_t)processorMask.Mask;
+ while (mask)
+ {
+ procsOnNode++;
+ mask &= mask - 1;
+ }
+
+ currentProcsOnNode = max(currentProcsOnNode, procsOnNode);
+ }
+ }
+
+ *max_procs_per_node = currentProcsOnNode;
+ *total_nodes = m_nNodes;
+ return true;
+ }
+
+ return false;
+}
+#else // HOST_WINDOWS
+/*static*/ BOOL NumaNodeInfo::GetNumaProcessorNodeEx(USHORT proc_no, PUSHORT node_no)
+{
+ return PAL_GetNumaProcessorNode(proc_no, node_no);
+}
+#endif // HOST_WINDOWS
+#endif
+
+/*static*/ BOOL NumaNodeInfo::m_enableGCNumaAware = FALSE;
+/*static*/ uint16_t NumaNodeInfo::m_nNodes = 0;
+/*static*/ BOOL NumaNodeInfo::InitNumaNodeInfoAPI()
+{
+#if !defined(FEATURE_REDHAWK)
+ //check for numa support if multiple heaps are used
+ ULONG highest = 0;
+
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_GCNumaAware) == 0)
+ return FALSE;
+
+ // fail to get the highest numa node number
+ if (!::GetNumaHighestNodeNumber(&highest) || (highest == 0))
+ return FALSE;
+
+ m_nNodes = (USHORT)(highest + 1);
+
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ BOOL NumaNodeInfo::CanEnableGCNumaAware()
+{
+ return m_enableGCNumaAware;
+}
+
+/*static*/ void NumaNodeInfo::InitNumaNodeInfo()
+{
+ m_enableGCNumaAware = InitNumaNodeInfoAPI();
+}
+
+#ifdef HOST_WINDOWS
+
+//******************************************************************************
+// CPUGroupInfo
+//******************************************************************************
+#if !defined(FEATURE_REDHAWK)
+/*static*/ //CPUGroupInfo::PNTQSIEx CPUGroupInfo::m_pNtQuerySystemInformationEx = NULL;
+
+/*static*/ BOOL CPUGroupInfo::GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP relationship,
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *slpiex, PDWORD count)
+{
+ LIMITED_METHOD_CONTRACT;
+ return ::GetLogicalProcessorInformationEx(relationship, slpiex, count);
+}
+
+/*static*/ BOOL CPUGroupInfo::SetThreadGroupAffinity(HANDLE h,
+ const GROUP_AFFINITY *groupAffinity, GROUP_AFFINITY *previousGroupAffinity)
+{
+ LIMITED_METHOD_CONTRACT;
+ return ::SetThreadGroupAffinity(h, groupAffinity, previousGroupAffinity);
+}
+
+/*static*/ BOOL CPUGroupInfo::GetThreadGroupAffinity(HANDLE h, GROUP_AFFINITY *groupAffinity)
+{
+ LIMITED_METHOD_CONTRACT;
+ return ::GetThreadGroupAffinity(h, groupAffinity);
+}
+
+/*static*/ BOOL CPUGroupInfo::GetSystemTimes(FILETIME *idleTime, FILETIME *kernelTime, FILETIME *userTime)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef HOST_WINDOWS
+ return ::GetSystemTimes(idleTime, kernelTime, userTime);
+#else
+ return FALSE;
+#endif
+}
+#endif
+
+/*static*/ BOOL CPUGroupInfo::m_enableGCCPUGroups = FALSE;
+/*static*/ BOOL CPUGroupInfo::m_threadUseAllCpuGroups = FALSE;
+/*static*/ BOOL CPUGroupInfo::m_threadAssignCpuGroups = FALSE;
+/*static*/ WORD CPUGroupInfo::m_nGroups = 0;
+/*static*/ WORD CPUGroupInfo::m_nProcessors = 0;
+/*static*/ WORD CPUGroupInfo::m_initialGroup = 0;
+/*static*/ CPU_Group_Info *CPUGroupInfo::m_CPUGroupInfoArray = NULL;
+/*static*/ LONG CPUGroupInfo::m_initialization = 0;
+/*static*/ bool CPUGroupInfo::s_hadSingleProcessorAtStartup = false;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+// Calculate greatest common divisor
+DWORD GCD(DWORD u, DWORD v)
+{
+ while (v != 0)
+ {
+ DWORD dwTemp = v;
+ v = u % v;
+ u = dwTemp;
+ }
+
+ return u;
+}
+
+// Calculate least common multiple
+DWORD LCM(DWORD u, DWORD v)
+{
+ return u / GCD(u, v) * v;
+}
+#endif
+
+/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoArray()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ BYTE *bBuffer = NULL;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pSLPIEx = NULL;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pRecord = NULL;
+ DWORD cbSLPIEx = 0;
+ DWORD byteOffset = 0;
+ DWORD dwNumElements = 0;
+ DWORD dwWeight = 1;
+
+ if (CPUGroupInfo::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return FALSE;
+
+ _ASSERTE(cbSLPIEx);
+
+ // Fail to allocate buffer
+ bBuffer = new (nothrow) BYTE[ cbSLPIEx ];
+ if (bBuffer == NULL)
+ return FALSE;
+
+ pSLPIEx = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)bBuffer;
+ if (!::GetLogicalProcessorInformationEx(RelationGroup, pSLPIEx, &cbSLPIEx))
+ {
+ delete[] bBuffer;
+ return FALSE;
+ }
+
+ pRecord = pSLPIEx;
+ while (byteOffset < cbSLPIEx)
+ {
+ if (pRecord->Relationship == RelationGroup)
+ {
+ m_nGroups = pRecord->Group.ActiveGroupCount;
+ break;
+ }
+ byteOffset += pRecord->Size;
+ pRecord = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)(bBuffer + byteOffset);
+ }
+
+ m_CPUGroupInfoArray = new (nothrow) CPU_Group_Info[m_nGroups];
+ if (m_CPUGroupInfoArray == NULL)
+ {
+ delete[] bBuffer;
+ return FALSE;
+ }
+
+ for (DWORD i = 0; i < m_nGroups; i++)
+ {
+ m_CPUGroupInfoArray[i].nr_active = (WORD)pRecord->Group.GroupInfo[i].ActiveProcessorCount;
+ m_CPUGroupInfoArray[i].active_mask = pRecord->Group.GroupInfo[i].ActiveProcessorMask;
+ m_nProcessors += m_CPUGroupInfoArray[i].nr_active;
+ dwWeight = LCM(dwWeight, (DWORD)m_CPUGroupInfoArray[i].nr_active);
+ }
+
+ // The number of threads per group that can be supported will depend on the number of CPU groups
+ // and the number of LPs within each processor group. For example, when the number of LPs in
+ // CPU groups is the same and is 64, the number of threads per group before weight overflow
+ // would be 2^32/2^6 = 2^26 (64M threads)
+ for (DWORD i = 0; i < m_nGroups; i++)
+ {
+ m_CPUGroupInfoArray[i].groupWeight = dwWeight / (DWORD)m_CPUGroupInfoArray[i].nr_active;
+ m_CPUGroupInfoArray[i].activeThreadWeight = 0;
+ }
+
+ delete[] bBuffer; // done with it; free it
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ BOOL CPUGroupInfo::InitCPUGroupInfoRange()
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ WORD begin = 0;
+ WORD nr_proc = 0;
+
+ for (WORD i = 0; i < m_nGroups; i++)
+ {
+ nr_proc += m_CPUGroupInfoArray[i].nr_active;
+ m_CPUGroupInfoArray[i].begin = begin;
+ m_CPUGroupInfoArray[i].end = nr_proc - 1;
+ begin = nr_proc;
+ }
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+/*static*/ void CPUGroupInfo::InitCPUGroupInfo()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ BOOL enableGCCPUGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCCpuGroup) != 0;
+ BOOL threadUseAllCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_UseAllCpuGroups) != 0;
+ BOOL threadAssignCpuGroups = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_Thread_AssignCpuGroups) != 0;
+
+ if (!enableGCCPUGroups)
+ return;
+
+ if (!InitCPUGroupInfoArray())
+ return;
+
+ if (!InitCPUGroupInfoRange())
+ return;
+
+ // initalGroup is whatever the CPU group that the main thread is running on
+ GROUP_AFFINITY groupAffinity;
+ CPUGroupInfo::GetThreadGroupAffinity(GetCurrentThread(), &groupAffinity);
+ m_initialGroup = groupAffinity.Group;
+
+ // only enable CPU groups if more than one group exists
+ BOOL hasMultipleGroups = m_nGroups > 1;
+ m_enableGCCPUGroups = enableGCCPUGroups && hasMultipleGroups;
+ m_threadUseAllCpuGroups = threadUseAllCpuGroups && hasMultipleGroups;
+ m_threadAssignCpuGroups = threadAssignCpuGroups && hasMultipleGroups;
+#endif // TARGET_AMD64 || TARGET_ARM64
+
+ // Determine if the process is affinitized to a single processor (or if the system has a single processor)
+ DWORD_PTR processAffinityMask, systemAffinityMask;
+ if (GetProcessAffinityMask(GetCurrentProcess(), &processAffinityMask, &systemAffinityMask))
+ {
+ processAffinityMask &= systemAffinityMask;
+ if (processAffinityMask != 0 && // only one CPU group is involved
+ (processAffinityMask & (processAffinityMask - 1)) == 0) // only one bit is set
+ {
+ s_hadSingleProcessorAtStartup = true;
+ }
+ }
+}
+
+/*static*/ BOOL CPUGroupInfo::IsInitialized()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (m_initialization == -1);
+}
+
+/*static*/ void CPUGroupInfo::EnsureInitialized()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ // CPUGroupInfo needs to be initialized only once. This could happen in three cases
+ // 1. CLR initialization at beginning of EEStartup, or
+ // 2. Sometimes, when hosted by ASP.NET, the hosting process may initialize ThreadPool
+ // before initializing CLR, thus require CPUGroupInfo to be initialized to determine
+ // if CPU group support should/could be enabled.
+ // 3. Call into Threadpool functions before Threadpool _and_ CLR is initialized.
+ // Vast majority of time, CPUGroupInfo is initialized in case 1. or 2.
+ // The chance of contention will be extremely small, so the following code should be fine
+ //
+retry:
+ if (IsInitialized())
+ return;
+
+ if (InterlockedCompareExchange(&m_initialization, 1, 0) == 0)
+ {
+ InitCPUGroupInfo();
+ m_initialization = -1;
+ }
+ else //some other thread started initialization, just wait until complete;
+ {
+ while (m_initialization != -1)
+ {
+ SwitchToThread();
+ }
+ goto retry;
+ }
+}
+
+/*static*/ WORD CPUGroupInfo::GetNumActiveProcessors()
+{
+ LIMITED_METHOD_CONTRACT;
+ return (WORD)m_nProcessors;
+}
+
+/*static*/ void CPUGroupInfo::GetGroupForProcessor(WORD processor_number,
+ WORD* group_number, WORD* group_processor_number)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ WORD bTemp = 0;
+ WORD bDiff = processor_number - bTemp;
+
+ for (WORD i=0; i < m_nGroups; i++)
+ {
+ bTemp += m_CPUGroupInfoArray[i].nr_active;
+ if (bTemp > processor_number)
+ {
+ *group_number = i;
+ *group_processor_number = bDiff;
+
+ break;
+ }
+ bDiff = processor_number - bTemp;
+ }
+#else
+ *group_number = 0;
+ *group_processor_number = 0;
+#endif
+}
+
+/*static*/ DWORD CPUGroupInfo::CalculateCurrentProcessorNumber()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if !defined(FEATURE_REDHAWK) && (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ // m_enableGCCPUGroups and m_threadUseAllCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups);
+
+ PROCESSOR_NUMBER proc_no;
+ proc_no.Group=0;
+ proc_no.Number=0;
+ proc_no.Reserved=0;
+ ::GetCurrentProcessorNumberEx(&proc_no);
+
+ DWORD fullNumber = 0;
+ for (WORD i = 0; i < proc_no.Group; i++)
+ fullNumber += (DWORD)m_CPUGroupInfoArray[i].nr_active;
+ fullNumber += (DWORD)(proc_no.Number);
+
+ return fullNumber;
+#else
+ return 0;
+#endif
+}
+
+// There can be different numbers of procs in groups. We take the max.
+/*static*/ bool CPUGroupInfo::GetCPUGroupInfo(PUSHORT total_groups, DWORD* max_procs_per_group)
+{
+ if (m_enableGCCPUGroups)
+ {
+ *total_groups = m_nGroups;
+ DWORD currentProcsInGroup = 0;
+ for (WORD i = 0; i < m_nGroups; i++)
+ {
+ currentProcsInGroup = max(currentProcsInGroup, m_CPUGroupInfoArray[i].nr_active);
+ }
+ *max_procs_per_group = currentProcsInGroup;
+ return true;
+ }
+
+ return false;
+}
+
+#if !defined(FEATURE_REDHAWK)
+//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
+/*static*/ void CPUGroupInfo::ChooseCPUGroupAffinity(GROUP_AFFINITY *gf)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ WORD i, minGroup = 0;
+ DWORD minWeight = 0;
+
+ // m_enableGCCPUGroups, m_threadUseAllCpuGroups, and m_threadAssignCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups && m_threadAssignCpuGroups);
+
+ for (i = 0; i < m_nGroups; i++)
+ {
+ minGroup = (m_initialGroup + i) % m_nGroups;
+
+ // the group is not filled up, use it
+ if (m_CPUGroupInfoArray[minGroup].activeThreadWeight / m_CPUGroupInfoArray[minGroup].groupWeight
+ < (DWORD)m_CPUGroupInfoArray[minGroup].nr_active)
+ goto found;
+ }
+
+ // all groups filled up, distribute proportionally
+ minGroup = m_initialGroup;
+ minWeight = m_CPUGroupInfoArray[m_initialGroup].activeThreadWeight;
+ for (i = 0; i < m_nGroups; i++)
+ {
+ if (m_CPUGroupInfoArray[i].activeThreadWeight < minWeight)
+ {
+ minGroup = i;
+ minWeight = m_CPUGroupInfoArray[i].activeThreadWeight;
+ }
+ }
+
+found:
+ gf->Group = minGroup;
+ gf->Mask = m_CPUGroupInfoArray[minGroup].active_mask;
+ gf->Reserved[0] = 0;
+ gf->Reserved[1] = 0;
+ gf->Reserved[2] = 0;
+ m_CPUGroupInfoArray[minGroup].activeThreadWeight += m_CPUGroupInfoArray[minGroup].groupWeight;
+#endif
+}
+
+//Lock ThreadStore before calling this function, so that updates of weights/counts are consistent
+/*static*/ void CPUGroupInfo::ClearCPUGroupAffinity(GROUP_AFFINITY *gf)
+{
+ LIMITED_METHOD_CONTRACT;
+#if (defined(TARGET_AMD64) || defined(TARGET_ARM64))
+ // m_enableGCCPUGroups, m_threadUseAllCpuGroups, and m_threadAssignCpuGroups must be TRUE
+ _ASSERTE(m_enableGCCPUGroups && m_threadUseAllCpuGroups && m_threadAssignCpuGroups);
+
+ WORD group = gf->Group;
+ m_CPUGroupInfoArray[group].activeThreadWeight -= m_CPUGroupInfoArray[group].groupWeight;
+#endif
+}
+
+BOOL CPUGroupInfo::GetCPUGroupRange(WORD group_number, WORD* group_begin, WORD* group_size)
+{
+ if (group_number >= m_nGroups)
+ {
+ return FALSE;
+ }
+
+ *group_begin = m_CPUGroupInfoArray[group_number].begin;
+ *group_size = m_CPUGroupInfoArray[group_number].nr_active;
+
+ return TRUE;
+}
+
+#endif
+
+/*static*/ BOOL CPUGroupInfo::CanEnableGCCPUGroups()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_enableGCCPUGroups;
+}
+
+/*static*/ BOOL CPUGroupInfo::CanEnableThreadUseAllCpuGroups()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_threadUseAllCpuGroups;
+}
+
+/*static*/ BOOL CPUGroupInfo::CanAssignCpuGroupsToThreads()
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_threadAssignCpuGroups;
+}
+#endif // HOST_WINDOWS
+
+//******************************************************************************
+// Returns the number of processors that a process has been configured to run on
+//******************************************************************************
+int GetCurrentProcessCpuCount()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ static int cCPUs = 0;
+
+ if (cCPUs != 0)
+ return cCPUs;
+
+ unsigned int count = 0;
+
+#ifdef HOST_WINDOWS
+ DWORD_PTR pmask, smask;
+
+ if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
+ {
+ count = 1;
+ }
+ else
+ {
+ pmask &= smask;
+
+ while (pmask)
+ {
+ pmask &= (pmask - 1);
+ count++;
+ }
+
+ // GetProcessAffinityMask can return pmask=0 and smask=0 on systems with more
+ // than 64 processors, which would leave us with a count of 0. Since the GC
+ // expects there to be at least one processor to run on (and thus at least one
+ // heap), we'll return 64 here if count is 0, since there are likely a ton of
+ // processors available in that case. The GC also cannot (currently) handle
+ // the case where there are more than 64 processors, so we will return a
+ // maximum of 64 here.
+ if (count == 0 || count > 64)
+ count = 64;
+ }
+
+#else // HOST_WINDOWS
+ count = PAL_GetLogicalCpuCountFromOS();
+
+ uint32_t cpuLimit;
+ if (PAL_GetCpuLimit(&cpuLimit) && cpuLimit < count)
+ count = cpuLimit;
+#endif // HOST_WINDOWS
+
+ cCPUs = count;
+
+ return count;
+}
+
+#ifdef HOST_WINDOWS
+DWORD_PTR GetCurrentProcessCpuMask()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ CANNOT_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+#ifdef HOST_WINDOWS
+ DWORD_PTR pmask, smask;
+
+ if (!GetProcessAffinityMask(GetCurrentProcess(), &pmask, &smask))
+ return 1;
+
+ pmask &= smask;
+ return pmask;
+#else
+ return 0;
+#endif
+}
+#endif // HOST_WINDOWS
+
+uint32_t GetOsPageSizeUncached()
+{
+ SYSTEM_INFO sysInfo;
+ ::GetSystemInfo(&sysInfo);
+ return sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x1000;
+}
+
+namespace
+{
+ Volatile<uint32_t> g_pageSize = 0;
+}
+
+uint32_t GetOsPageSize()
+{
+#ifdef HOST_UNIX
+ size_t result = g_pageSize.LoadWithoutBarrier();
+
+ if(!result)
+ {
+ result = GetOsPageSizeUncached();
+
+ g_pageSize.StoreWithoutBarrier(result);
+ }
+
+ return result;
+#else
+ return 0x1000;
+#endif
+}
+
+/**************************************************************************/
+
+/**************************************************************************/
+void ConfigMethodSet::init(const CLRConfig::ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ THROWS;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ LPWSTR str = CLRConfig::GetConfigValue(info);
+ if (str)
+ {
+ m_list.Insert(str);
+ delete[] str;
+ }
+ m_inited = 1;
+}
+
+/**************************************************************************/
+bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, PCCOR_SIGNATURE sig)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_inited == 1);
+
+ if (m_list.IsEmpty())
+ return false;
+ return(m_list.IsInList(methodName, className, sig));
+}
+
+/**************************************************************************/
+bool ConfigMethodSet::contains(LPCUTF8 methodName, LPCUTF8 className, CORINFO_SIG_INFO* pSigInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(m_inited == 1);
+
+ if (m_list.IsEmpty())
+ return false;
+ return(m_list.IsInList(methodName, className, pSigInfo));
+}
+
+/**************************************************************************/
+void ConfigDWORD::init_DontUse_(__in_z LPCWSTR keyName, DWORD defaultVal)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ m_value = REGUTIL::GetConfigDWORD_DontUse_(keyName, defaultVal);
+ m_inited = 1;
+}
+
+/**************************************************************************/
+void ConfigString::init(const CLRConfig::ConfigStringInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ // Note: m_value will be leaking
+ m_value = CLRConfig::GetConfigValue(info);
+ m_inited = 1;
+}
+
+//=============================================================================
+// AssemblyNamesList
+//=============================================================================
+// The string should be of the form
+// MyAssembly
+// MyAssembly;mscorlib;System
+// MyAssembly;mscorlib System
+
+AssemblyNamesList::AssemblyNamesList(__in LPWSTR list)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ WCHAR prevChar = '?'; // dummy
+ LPWSTR nameStart = NULL; // start of the name currently being processed. NULL if no current name
+ AssemblyName ** ppPrevLink = &m_pNames;
+
+ for (LPWSTR listWalk = list; prevChar != '\0'; prevChar = *listWalk, listWalk++)
+ {
+ WCHAR curChar = *listWalk;
+
+ if (iswspace(curChar) || curChar == ';' || curChar == '\0' )
+ {
+ //
+ // Found white-space
+ //
+
+ if (nameStart)
+ {
+ // Found the end of the current name
+
+ AssemblyName * newName = new AssemblyName();
+ size_t nameLen = listWalk - nameStart;
+
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ newName->m_assemblyName = new char[nameLen + 1];
+ memcpy(newName->m_assemblyName, temp, nameLen * sizeof(newName->m_assemblyName[0]));
+ newName->m_assemblyName[nameLen] = '\0';
+
+ *ppPrevLink = newName;
+ ppPrevLink = &newName->m_next;
+
+ nameStart = NULL;
+ }
+ }
+ else if (!nameStart)
+ {
+ //
+ // Found the start of a new name
+ //
+
+ nameStart = listWalk;
+ }
+ }
+
+ _ASSERTE(!nameStart); // cannot be in the middle of a name
+ *ppPrevLink = NULL;
+}
+
+AssemblyNamesList::~AssemblyNamesList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ for (AssemblyName * pName = m_pNames; pName; /**/)
+ {
+ AssemblyName * cur = pName;
+ pName = pName->m_next;
+
+ delete [] cur->m_assemblyName;
+ delete cur;
+ }
+}
+
+bool AssemblyNamesList::IsInList(LPCUTF8 assemblyName)
+{
+ if (IsEmpty())
+ return false;
+
+ for (AssemblyName * pName = m_pNames; pName; pName = pName->m_next)
+ {
+ if (_stricmp(pName->m_assemblyName, assemblyName) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// MethodNamesList
+//=============================================================================
+// str should be of the form :
+// "foo1 MyNamespace.MyClass:foo3 *:foo4 foo5(x,y,z)"
+// "MyClass:foo2 MyClass:*" will match under _DEBUG
+//
+
+void MethodNamesListBase::Insert(__in_z LPWSTR str)
+{
+ CONTRACTL {
+ THROWS;
+ } CONTRACTL_END;
+
+ enum State { NO_NAME, CLS_NAME, FUNC_NAME, ARG_LIST }; // parsing state machine
+
+ const char SEP_CHAR = ' '; // current character use to separate each entry
+// const char SEP_CHAR = ';'; // better character use to separate each entry
+
+ WCHAR lastChar = '?'; // dummy
+ LPWSTR nameStart = NULL; // while walking over the classname or methodname, this points to start
+ MethodName nameBuf; // Buffer used while parsing the current entry
+ MethodName** lastName = &pNames; // last entry inserted into the list
+ bool bQuote = false;
+
+ nameBuf.methodName = NULL;
+ nameBuf.className = NULL;
+ nameBuf.numArgs = -1;
+ nameBuf.next = NULL;
+
+ for(State state = NO_NAME; lastChar != '\0'; str++)
+ {
+ lastChar = *str;
+
+ switch(state)
+ {
+ case NO_NAME:
+ if (*str != SEP_CHAR)
+ {
+ nameStart = str;
+ state = CLS_NAME; // we have found the start of the next entry
+ }
+ break;
+
+ case CLS_NAME:
+ if (*nameStart == '"')
+ {
+ while (*str && *str!='"')
+ {
+ str++;
+ }
+ nameStart++;
+ bQuote=true;
+ }
+
+ if (*str == ':')
+ {
+ if (*nameStart == '*' && !bQuote)
+ {
+ // Is the classname string a wildcard. Then set it to NULL
+ nameBuf.className = NULL;
+ }
+ else
+ {
+ int len = (int)(str - nameStart);
+
+ // Take off the quote
+ if (bQuote) { len--; bQuote=false; }
+
+ nameBuf.className = new char[len + 1];
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ memcpy(nameBuf.className, temp, len*sizeof(nameBuf.className[0]));
+ nameBuf.className[len] = '\0';
+ }
+ if (str[1] == ':') // Accept class::name syntax too
+ str++;
+ nameStart = str + 1;
+ state = FUNC_NAME;
+ }
+ else if (*str == '\0' || *str == SEP_CHAR || *str == '(')
+ {
+ /* This was actually a method name without any class */
+ nameBuf.className = NULL;
+ goto DONE_FUNC_NAME;
+ }
+ break;
+
+ case FUNC_NAME:
+ if (*nameStart == '"')
+ {
+ while ( (nameStart==str) || // workaround to handle when className!=NULL
+ (*str && *str!='"'))
+ {
+ str++;
+ }
+
+ nameStart++;
+ bQuote=true;
+ }
+
+ if (*str == '\0' || *str == SEP_CHAR || *str == '(')
+ {
+ DONE_FUNC_NAME:
+ _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == '(');
+
+ if (*nameStart == '*' && !bQuote)
+ {
+ // Is the name string a wildcard. Then set it to NULL
+ nameBuf.methodName = NULL;
+ }
+ else
+ {
+ int len = (int)(str - nameStart);
+
+ // Take off the quote
+ if (bQuote) { len--; bQuote=false; }
+
+ nameBuf.methodName = new char[len + 1];
+ MAKE_UTF8PTR_FROMWIDE(temp, nameStart);
+ memcpy(nameBuf.methodName, temp, len*sizeof(nameBuf.methodName[0]));
+ nameBuf.methodName[len] = '\0';
+ }
+
+ if (*str == '\0' || *str == SEP_CHAR)
+ {
+ nameBuf.numArgs = -1;
+ goto DONE_ARG_LIST;
+ }
+ else
+ {
+ _ASSERTE(*str == '(');
+ nameBuf.numArgs = -1;
+ state = ARG_LIST;
+ }
+ }
+ break;
+
+ case ARG_LIST:
+ if (*str == '\0' || *str == ')')
+ {
+ if (nameBuf.numArgs == -1)
+ nameBuf.numArgs = 0;
+
+ DONE_ARG_LIST:
+ _ASSERTE(*str == '\0' || *str == SEP_CHAR || *str == ')');
+
+ // We have parsed an entire method name.
+ // Create a new entry in the list for it
+
+ MethodName * newName = new MethodName();
+ *newName = nameBuf;
+ newName->next = NULL;
+ *lastName = newName;
+ lastName = &newName->next;
+ state = NO_NAME;
+
+ // Skip anything after the argument list until we find the next
+ // separator character, otherwise if we see "func(a,b):foo" we
+ // create entries for "func(a,b)" as well as ":foo".
+ if (*str == ')')
+ {
+ while (*str && *str != SEP_CHAR)
+ {
+ str++;
+ }
+ lastChar = *str;
+ }
+ }
+ else
+ {
+ if (*str != SEP_CHAR && nameBuf.numArgs == -1)
+ nameBuf.numArgs = 1;
+ if (*str == ',')
+ nameBuf.numArgs++;
+ }
+ break;
+
+ default: _ASSERTE(!"Bad state"); break;
+ }
+ }
+}
+
+/**************************************************************/
+
+void MethodNamesListBase::Destroy()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ for(MethodName * pName = pNames; pName; /**/)
+ {
+ if (pName->className)
+ delete [] pName->className;
+ if (pName->methodName)
+ delete [] pName->methodName;
+
+ MethodName * curName = pName;
+ pName = pName->next;
+ delete curName;
+ }
+}
+
+/**************************************************************/
+bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, PCCOR_SIGNATURE sig)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int numArgs = -1;
+ if (sig != NULL)
+ {
+ sig++; // Skip calling convention
+ numArgs = CorSigUncompressData(sig);
+ }
+
+ return IsInList(methName, clsName, numArgs);
+}
+
+/**************************************************************/
+bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, CORINFO_SIG_INFO* pSigInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int numArgs = -1;
+ if (pSigInfo != NULL)
+ {
+ numArgs = pSigInfo->numArgs;
+ }
+
+ return IsInList(methName, clsName, numArgs);
+}
+
+/**************************************************************/
+bool MethodNamesListBase::IsInList(LPCUTF8 methName, LPCUTF8 clsName, int numArgs)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // Try to match all the entries in the list
+
+ for(MethodName * pName = pNames; pName; pName = pName->next)
+ {
+ // If numArgs is valid, check for mismatch
+ if (pName->numArgs != -1 && pName->numArgs != numArgs)
+ continue;
+
+ // If methodName is valid, check for mismatch
+ if (pName->methodName) {
+ if (strcmp(pName->methodName, methName) != 0) {
+
+ // C++ embeds the class name into the method name,
+ // deal with that here (workaround)
+ const char* ptr = strchr(methName, ':');
+ if (ptr != 0 && ptr[1] == ':' && strcmp(&ptr[2], pName->methodName) == 0) {
+ unsigned clsLen = (unsigned)(ptr - methName);
+ if (pName->className == 0 || strncmp(pName->className, methName, clsLen) == 0)
+ return true;
+ }
+ continue;
+ }
+ }
+
+ // check for class Name exact match
+ if (clsName == 0 || pName->className == 0 || strcmp(pName->className, clsName) == 0)
+ return true;
+
+ // check for suffix wildcard like System.*
+ unsigned len = (unsigned)strlen(pName->className);
+ if (len > 0 && pName->className[len-1] == '*' && strncmp(pName->className, clsName, len-1) == 0)
+ return true;
+
+#ifdef _DEBUG
+ // Maybe className doesnt include namespace. Try to match that
+ LPCUTF8 onlyClass = ns::FindSep(clsName);
+ if (onlyClass && strcmp(pName->className, onlyClass+1) == 0)
+ return true;
+#endif
+ }
+ return(false);
+}
+
+//=============================================================================
+// Signature Validation Functions (scaled down version from MDValidator
+//=============================================================================
+
+//*****************************************************************************
+// This function validates one argument given an offset into the signature
+// where the argument begins. This function assumes that the signature is well
+// formed as far as the compression scheme is concerned.
+// <TODO>@todo: Validate tokens embedded.</TODO>
+//*****************************************************************************
+HRESULT validateOneArg(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ SigParser *pSig,
+ ULONG *pulNSentinels, // [IN/OUT] Number of sentinels
+ IMDInternalImport* pImport, // [IN] Internal MD Import interface ptr
+ BOOL bNoVoidAllowed) // [IN] Flag indicating whether "void" is disallowed for this arg
+
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ BYTE elementType; // Current element type being processed.
+ mdToken token; // Embedded token.
+ ULONG ulArgCnt; // Argument count for function pointer.
+ ULONG ulIndex; // Index for type parameters
+ ULONG ulRank; // Rank of the array.
+ ULONG ulSizes; // Count of sized dimensions of the array.
+ ULONG ulLbnds; // Count of lower bounds of the array.
+ ULONG ulCallConv;
+
+ HRESULT hr = S_OK; // Value returned.
+ BOOL bRepeat = TRUE; // MODOPT and MODREQ belong to the arg after them
+
+ while(bRepeat)
+ {
+ bRepeat = FALSE;
+ // Validate that the argument is not missing.
+
+ // Get the element type.
+ if (FAILED(pSig->GetByte(&elementType)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSARG);
+ }
+
+ // Walk past all the modifier types.
+ while (elementType & ELEMENT_TYPE_MODIFIER)
+ {
+ if (elementType == ELEMENT_TYPE_SENTINEL)
+ {
+ if(pulNSentinels) *pulNSentinels+=1;
+ if(TypeFromToken(tk) != mdtMemberRef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
+ }
+ if (FAILED(pSig->GetByte(&elementType)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSELTYPE);
+ }
+ }
+
+ switch (elementType)
+ {
+ case ELEMENT_TYPE_VOID:
+ if(bNoVoidAllowed) IfFailGo(VLDTR_E_SIG_BADVOID);
+ FALLTHROUGH;
+
+ case ELEMENT_TYPE_BOOLEAN:
+ case ELEMENT_TYPE_CHAR:
+ case ELEMENT_TYPE_I1:
+ case ELEMENT_TYPE_U1:
+ case ELEMENT_TYPE_I2:
+ case ELEMENT_TYPE_U2:
+ case ELEMENT_TYPE_I4:
+ case ELEMENT_TYPE_U4:
+ case ELEMENT_TYPE_I8:
+ case ELEMENT_TYPE_U8:
+ case ELEMENT_TYPE_R4:
+ case ELEMENT_TYPE_R8:
+ case ELEMENT_TYPE_STRING:
+ case ELEMENT_TYPE_OBJECT:
+ case ELEMENT_TYPE_TYPEDBYREF:
+ case ELEMENT_TYPE_U:
+ case ELEMENT_TYPE_I:
+ break;
+ case ELEMENT_TYPE_PTR:
+ // Validate the referenced type.
+ if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, FALSE))) IfFailGo(hr);
+ break;
+ case ELEMENT_TYPE_BYREF: //fallthru
+ if(TypeFromToken(tk)==mdtFieldDef) IfFailGo(VLDTR_E_SIG_BYREFINFIELD);
+ FALLTHROUGH;
+ case ELEMENT_TYPE_PINNED:
+ case ELEMENT_TYPE_SZARRAY:
+ // Validate the referenced type.
+ if(FAILED(hr = validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE))) IfFailGo(hr);
+ break;
+ case ELEMENT_TYPE_CMOD_OPT:
+ case ELEMENT_TYPE_CMOD_REQD:
+ bRepeat = TRUE; // go on validating, we're not done with this arg
+ FALLTHROUGH;
+ case ELEMENT_TYPE_VALUETYPE: //fallthru
+ case ELEMENT_TYPE_CLASS:
+ // See if the token is missing.
+ if (FAILED(pSig->GetToken(&token)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSTKN);
+ }
+ // Token validation .
+ if(pImport)
+ {
+ ULONG rid = RidFromToken(token);
+ ULONG typ = TypeFromToken(token);
+ ULONG maxrid = pImport->GetCountWithTokenKind(typ);
+ if(typ == mdtTypeDef) maxrid++;
+ if((rid==0)||(rid > maxrid)) IfFailGo(VLDTR_E_SIG_TKNBAD);
+ }
+ break;
+
+ case ELEMENT_TYPE_FNPTR:
+ // <TODO>@todo: More function pointer validation?</TODO>
+ // Validate that calling convention is present.
+ if (FAILED(pSig->GetCallingConvInfo(&ulCallConv)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTR);
+ }
+ if(((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) >= IMAGE_CEE_CS_CALLCONV_MAX)
+ ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) IfFailGo(VLDTR_E_MD_BADCALLINGCONV);
+
+ // Validate that argument count is present.
+ if (FAILED(pSig->GetData(&ulArgCnt)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ // FNPTR signature must follow the rules of MethodDef
+ // Validate and consume return type.
+ IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, FALSE));
+
+ // Validate and consume the arguments.
+ while(ulArgCnt--)
+ {
+ IfFailGo(validateOneArg(mdtMethodDef, pSig, NULL, pImport, TRUE));
+ }
+ break;
+
+ case ELEMENT_TYPE_ARRAY:
+ // Validate and consume the base type.
+ IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
+
+ // Validate that the rank is present.
+ if (FAILED(pSig->GetData(&ulRank)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSRANK);
+ }
+
+ // Process the sizes.
+ if (ulRank)
+ {
+ // Validate that the count of sized-dimensions is specified.
+ if (FAILED(pSig->GetData(&ulSizes)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSNSIZE);
+ }
+
+ // Loop over the sizes.
+ while(ulSizes--)
+ {
+ // Validate the current size.
+ if (FAILED(pSig->GetData(NULL)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSSIZE);
+ }
+ }
+
+ // Validate that the count of lower bounds is specified.
+ if (FAILED(pSig->GetData(&ulLbnds)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSNLBND);
+ }
+
+ // Loop over the lower bounds.
+ while(ulLbnds--)
+ {
+ // Validate the current lower bound.
+ if (FAILED(pSig->GetData(NULL)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSLBND);
+ }
+ }
+ }
+ break;
+ case ELEMENT_TYPE_VAR:
+ case ELEMENT_TYPE_MVAR:
+ // Validate that index is present.
+ if (FAILED(pSig->GetData(&ulIndex)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ //@todo GENERICS: check that index is in range
+ break;
+
+ case ELEMENT_TYPE_GENERICINST:
+ // Validate the generic type.
+ IfFailGo(validateOneArg(tk, pSig, pulNSentinels, pImport, TRUE));
+
+ // Validate that parameter count is present.
+ if (FAILED(pSig->GetData(&ulArgCnt)))
+ {
+ IfFailGo(VLDTR_E_SIG_MISSFPTRARGCNT);
+ }
+
+ //@todo GENERICS: check that number of parameters matches definition?
+
+ // Validate and consume the parameters.
+ while(ulArgCnt--)
+ {
+ IfFailGo(validateOneArg(tk, pSig, NULL, pImport, TRUE));
+ }
+ break;
+
+ case ELEMENT_TYPE_SENTINEL: // this case never works because all modifiers are skipped before switch
+ if(TypeFromToken(tk) == mdtMethodDef) IfFailGo(VLDTR_E_SIG_SENTINMETHODDEF);
+ break;
+
+ default:
+ IfFailGo(VLDTR_E_SIG_BADELTYPE);
+ break;
+ } // switch (ulElementType)
+ } // end while(bRepeat)
+ErrExit:
+ return hr;
+} // validateOneArg()
+
+//*****************************************************************************
+// This function validates the given Method/Field/Standalone signature.
+//@todo GENERICS: MethodInstantiation?
+//*****************************************************************************
+HRESULT validateTokenSig(
+ mdToken tk, // [IN] Token whose signature needs to be validated.
+ PCCOR_SIGNATURE pbSig, // [IN] Signature.
+ ULONG cbSig, // [IN] Size in bytes of the signature.
+ DWORD dwFlags, // [IN] Method flags.
+ IMDInternalImport* pImport) // [IN] Internal MD Import interface ptr
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ ULONG ulCallConv; // Calling convention.
+ ULONG ulArgCount = 1; // Count of arguments (1 because of the return type)
+ ULONG ulTyArgCount = 0; // Count of type arguments
+ ULONG ulArgIx = 0; // Starting index of argument (standalone sig: 1)
+ ULONG i; // Looping index.
+ HRESULT hr = S_OK; // Value returned.
+ ULONG ulNSentinels = 0;
+ SigParser sig(pbSig, cbSig);
+
+ _ASSERTE(TypeFromToken(tk) == mdtMethodDef ||
+ TypeFromToken(tk) == mdtMemberRef ||
+ TypeFromToken(tk) == mdtSignature ||
+ TypeFromToken(tk) == mdtFieldDef);
+
+ // Check for NULL signature.
+ if (!pbSig || !cbSig) return VLDTR_E_SIGNULL;
+
+ // Validate the calling convention.
+
+ // Moves behind calling convention
+ IfFailRet(sig.GetCallingConvInfo(&ulCallConv));
+ i = ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK;
+ switch(TypeFromToken(tk))
+ {
+ case mdtMethodDef: // MemberRefs have no flags available
+ // If HASTHIS is set on the calling convention, the method should not be static.
+ if ((ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ IsMdStatic(dwFlags)) return VLDTR_E_MD_THISSTATIC;
+
+ // If HASTHIS is not set on the calling convention, the method should be static.
+ if (!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS) &&
+ !IsMdStatic(dwFlags)) return VLDTR_E_MD_NOTTHISNOTSTATIC;
+ // fall thru to callconv check;
+ FALLTHROUGH;
+
+ case mdtMemberRef:
+ if(i == IMAGE_CEE_CS_CALLCONV_FIELD) return validateOneArg(tk, &sig, NULL, pImport, TRUE);
+
+ // EXPLICITTHIS and native call convs are for stand-alone sigs only (for calli)
+ if(((i != IMAGE_CEE_CS_CALLCONV_DEFAULT)&&( i != IMAGE_CEE_CS_CALLCONV_VARARG))
+ || (ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)) return VLDTR_E_MD_BADCALLINGCONV;
+ break;
+
+ case mdtSignature:
+ if(i != IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) // then it is function sig for calli
+ {
+ if((i >= IMAGE_CEE_CS_CALLCONV_MAX)
+ ||((ulCallConv & IMAGE_CEE_CS_CALLCONV_EXPLICITTHIS)
+ &&(!(ulCallConv & IMAGE_CEE_CS_CALLCONV_HASTHIS)))) return VLDTR_E_MD_BADCALLINGCONV;
+ }
+ else
+ ulArgIx = 1; // Local variable signatures don't have a return type
+ break;
+
+ case mdtFieldDef:
+ if(i != IMAGE_CEE_CS_CALLCONV_FIELD) return VLDTR_E_MD_BADCALLINGCONV;
+ return validateOneArg(tk, &sig, NULL, pImport, TRUE);
+ }
+ // Is there any sig left for arguments?
+
+ // Get the type argument count
+ if (ulCallConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
+ {
+ if (FAILED(sig.GetData(&ulTyArgCount)))
+ {
+ return VLDTR_E_MD_NOARGCNT;
+ }
+ }
+
+ // Get the argument count.
+ if (FAILED(sig.GetData(&ulArgCount)))
+ {
+ return VLDTR_E_MD_NOARGCNT;
+ }
+
+ // Validate the return type and the arguments.
+ // (at this moment ulArgCount = num.args+1, ulArgIx = (standalone sig. ? 1 :0); )
+ for(; ulArgIx < ulArgCount; ulArgIx++)
+ {
+ if(FAILED(hr = validateOneArg(tk, &sig, &ulNSentinels, pImport, (ulArgIx!=0)))) return hr;
+ }
+
+ // <TODO>@todo: we allow junk to be at the end of the signature (we may not consume it all)
+ // do we care?</TODO>
+
+ if((ulNSentinels != 0) && ((ulCallConv & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_VARARG ))
+ return VLDTR_E_SIG_SENTMUSTVARARG;
+ if(ulNSentinels > 1) return VLDTR_E_SIG_MULTSENTINELS;
+ return S_OK;
+} // validateTokenSig()
+
+HRESULT GetImageRuntimeVersionString(PVOID pMetaData, LPCSTR* pString)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE(pString);
+ STORAGESIGNATURE* pSig = (STORAGESIGNATURE*) pMetaData;
+
+ // Verify the signature.
+
+ // If signature didn't match, you shouldn't be here.
+ if (pSig->GetSignature() != STORAGE_MAGIC_SIG)
+ return CLDB_E_FILE_CORRUPT;
+
+ // The version started in version 1.1
+ if (pSig->GetMajorVer() < 1)
+ return CLDB_E_FILE_OLDVER;
+
+ if (pSig->GetMajorVer() == 1 && pSig->GetMinorVer() < 1)
+ return CLDB_E_FILE_OLDVER;
+
+ // Header data starts after signature.
+ *pString = (LPCSTR) pSig->pVersion;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Convert a UTF8 string to Unicode, into a CQuickArray<WCHAR>.
+//*****************************************************************************
+HRESULT Utf2Quick(
+ LPCUTF8 pStr, // The string to convert.
+ CQuickArray<WCHAR> &rStr, // The QuickArray<WCHAR> to convert it into.
+ int iCurLen) // Inital characters in the array to leave (default 0).
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HRESULT hr = S_OK; // A result.
+ int iReqLen; // Required additional length.
+ int iActLen;
+ int bAlloc = 0; // If non-zero, allocation was required.
+
+ if (iCurLen < 0 )
+ {
+ _ASSERTE_MSG(false, "Invalid current length");
+ return E_INVALIDARG;
+ }
+
+ // Calculate the space available
+ S_SIZE_T cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
+ if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+
+ // Attempt the conversion.
+ LPWSTR rNewStr = rStr.Ptr()+iCurLen;
+ if(rNewStr < rStr.Ptr())
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
+
+ // If the buffer was too small, determine what is required.
+ if (iReqLen == 0)
+ bAlloc = iReqLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, 0, 0);
+ // Resize the buffer. If the buffer was large enough, this just sets the internal
+ // counter, but if it was too small, this will attempt a reallocation. Note that
+ // the length includes the terminating W('/0').
+ IfFailGo(rStr.ReSizeNoThrow(iCurLen+iReqLen));
+ // If we had to realloc, then do the conversion again, now that the buffer is
+ // large enough.
+ if (bAlloc) {
+ //recalculating cchAvail since MaxSize could have been changed.
+ cchAvail = S_SIZE_T(rStr.MaxSize()) - S_SIZE_T(iCurLen);
+ if (cchAvail.IsOverflow() || cchAvail.Value() > INT_MAX)
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ //reculculating rNewStr
+ rNewStr = rStr.Ptr()+iCurLen;
+
+ if(rNewStr < rStr.Ptr())
+ {
+ _ASSERTE_MSG(false, "Integer overflow/underflow");
+ return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
+ }
+ iActLen = WszMultiByteToWideChar(CP_UTF8, 0, pStr, -1, rNewStr, (int)(cchAvail.Value()));
+ _ASSERTE(iReqLen == iActLen);
+ }
+ErrExit:
+ return hr;
+} // HRESULT Utf2Quick()
+
+
+//*****************************************************************************
+// Extract the movl 64-bit unsigned immediate from an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+UINT64 GetIA64Imm64(UINT64 * pBundle)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Imm64(temp0, temp1);
+}
+
+UINT64 GetIA64Imm64(UINT64 qword0, UINT64 qword1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT64 imm64 = 0;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a movl opcode, with template 4 or 5
+ //
+ UINT64 templa = (qword0 >> 0) & 0x1f;
+ UINT64 opcode = (qword1 >> 60) & 0xf;
+
+ _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ imm64 = (qword1 >> 59) << 63; // 1 i
+ imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm41
+ imm64 |= (qword0 >> 46) << 22; // 18 low bits of imm41
+ imm64 |= (qword1 >> 23) & 0x200000; // 1 ic
+ imm64 |= (qword1 >> 29) & 0x1F0000; // 5 imm5c
+ imm64 |= (qword1 >> 43) & 0xFF80; // 9 imm9d
+ imm64 |= (qword1 >> 36) & 0x7F; // 7 imm7b
+
+ return imm64;
+}
+
+//*****************************************************************************
+// Deposit the movl 64-bit unsigned immediate into an IA64 bundle
+// (Format X2)
+//*****************************************************************************
+void PutIA64Imm64(UINT64 * pBundle, UINT64 imm64)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a movl opcode, with template 4 or 5
+ //
+ UINT64 templa = (pBundle[0] >> 0) & 0x1f;
+ UINT64 opcode = (pBundle[1] >> 60) & 0xf ;
+
+ _ASSERTE((opcode == 0x6) && ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xF000080FFF800000);
+
+ /* Clear all bits used as part of the imm64 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0;
+ UINT64 temp1;
+
+ temp1 = (imm64 >> 63) << 59; // 1 i
+ temp1 |= (imm64 & 0xFF80) << 43; // 9 imm9d
+ temp1 |= (imm64 & 0x1F0000) << 29; // 5 imm5c
+ temp1 |= (imm64 & 0x200000) << 23; // 1 ic
+ temp1 |= (imm64 & 0x7F) << 36; // 7 imm7b
+ temp1 |= (imm64 << 1) >> 41; // 23 high bits of imm41
+ temp0 = (imm64 >> 22) << 46; // 18 low bits of imm41
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the IP-Relative signed 25-bit immediate from an IA64 bundle
+// (Formats B1, B2 or B3)
+// Note that due to branch target alignment requirements
+// the lowest four bits in the result will always be zero.
+//*****************************************************************************
+INT32 GetIA64Rel25(UINT64 * pBundle, UINT32 slot)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Rel25(temp0, temp1, slot);
+}
+
+INT32 GetIA64Rel25(UINT64 qword0, UINT64 qword1, UINT32 slot)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ INT32 imm25 = 0;
+
+ if (slot == 2)
+ {
+ if ((qword1 >> 59) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword1 >> 32) & 0x00FFFFF0; // 20 imm20b
+ }
+ else if (slot == 1)
+ {
+ if ((qword1 >> 18) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword1 << 9) & 0x00FFFE00; // high 15 of imm20b
+ imm25 |= (qword0 >> 55) & 0x000001F0; // low 5 of imm20b
+ }
+ else if (slot == 0)
+ {
+ if ((qword0 >> 41) & 1)
+ imm25 = 0xFF000000;
+ imm25 |= (qword0 >> 14) & 0x00FFFFF0; // 20 imm20b
+ }
+
+ return imm25;
+}
+
+//*****************************************************************************
+// Deposit the IP-Relative signed 25-bit immediate into an IA64 bundle
+// (Formats B1, B2 or B3)
+// Note that due to branch target alignment requirements
+// the lowest four bits are required to be zero.
+//*****************************************************************************
+void PutIA64Rel25(UINT64 * pBundle, UINT32 slot, INT32 imm25)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ _ASSERTE((imm25 & 0xF) == 0);
+
+ if (slot == 2)
+ {
+ const UINT64 mask1 = UI64(0xF700000FFFFFFFFF);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[1] &= mask1;
+
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm25 & 0x1000000) << 35; // 1 s
+ temp1 |= (UINT64) (imm25 & 0x0FFFFF0) << 32; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[1] |= temp1;
+ }
+ else if (slot == 1)
+ {
+ const UINT64 mask0 = UI64(0x0EFFFFFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xFFFFFFFFFFFB8000);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0;
+ UINT64 temp1;
+
+ temp1 = (UINT64) (imm25 & 0x1000000) >> 7; // 1 s
+ temp1 |= (UINT64) (imm25 & 0x0FFFE00) >> 9; // high 15 of imm20b
+ temp0 = (UINT64) (imm25 & 0x00001F0) << 55; // low 5 of imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ }
+ else if (slot == 0)
+ {
+ const UINT64 mask0 = UI64(0xFFFFFDC00003FFFF);
+ /* Clear all bits used as part of the imm25 */
+ pBundle[0] &= mask0;
+
+ UINT64 temp0;
+
+ temp0 = (UINT64) (imm25 & 0x1000000) << 16; // 1 s
+ temp0 |= (UINT64) (imm25 & 0x0FFFFF0) << 14; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+
+ }
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the IP-Relative signed 64-bit immediate from an IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+INT64 GetIA64Rel64(UINT64 * pBundle)
+{
+ WRAPPER_NO_CONTRACT;
+
+ UINT64 temp0 = PTR_UINT64(pBundle)[0];
+ UINT64 temp1 = PTR_UINT64(pBundle)[1];
+
+ return GetIA64Rel64(temp0, temp1);
+}
+
+INT64 GetIA64Rel64(UINT64 qword0, UINT64 qword1)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ INT64 imm64 = 0;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a brl opcode, with template 4 or 5
+ //
+ UINT64 templa = (qword0 >> 0) & 0x1f;
+ UINT64 opcode = (qword1 >> 60) & 0xf;
+
+ _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
+ ((templa == 0x4) || (templa == 0x5)));
+#endif
+
+ imm64 = (qword1 >> 59) << 63; // 1 i
+ imm64 |= (qword1 << 41) >> 1; // 23 high bits of imm39
+ imm64 |= (qword0 >> 48) << 24; // 16 low bits of imm39
+ imm64 |= (qword1 >> 32) & 0xFFFFF0; // 20 imm20b
+ // 4 bits of zeros
+ return imm64;
+}
+
+//*****************************************************************************
+// Deposit the IP-Relative signed 64-bit immediate into an IA64 bundle
+// (Formats X3 or X4)
+//*****************************************************************************
+void PutIA64Rel64(UINT64 * pBundle, INT64 imm64)
+{
+ LIMITED_METHOD_CONTRACT;
+
+#ifdef _DEBUG_IMPL
+ //
+ // make certain we're decoding a brl opcode, with template 4 or 5
+ //
+ UINT64 templa = (pBundle[0] >> 0) & 0x1f;
+ UINT64 opcode = (pBundle[1] >> 60) & 0xf;
+
+ _ASSERTE(((opcode == 0xC) || (opcode == 0xD)) &&
+ ((templa == 0x4) || (templa == 0x5)));
+ _ASSERTE((imm64 & 0xF) == 0);
+#endif
+
+ const UINT64 mask0 = UI64(0x00003FFFFFFFFFFF);
+ const UINT64 mask1 = UI64(0xF700000FFF800000);
+
+ /* Clear all bits used as part of the imm64 */
+ pBundle[0] &= mask0;
+ pBundle[1] &= mask1;
+
+ UINT64 temp0 = (imm64 & UI64(0x000000FFFF000000)) << 24; // 16 low bits of imm39
+ UINT64 temp1 = (imm64 & UI64(0x8000000000000000)) >> 4 // 1 i
+ | (imm64 & UI64(0x7FFFFF0000000000)) >> 40 // 23 high bits of imm39
+ | (imm64 & UI64(0x0000000000FFFFF0)) << 32; // 20 imm20b
+
+ /* Or in the new bits used in the imm64 */
+ pBundle[0] |= temp0;
+ pBundle[1] |= temp1;
+ FlushInstructionCache(GetCurrentProcess(),pBundle,16);
+}
+
+//*****************************************************************************
+// Extract the 16-bit immediate from ARM Thumb2 Instruction (format T2_N)
+//*****************************************************************************
+static FORCEINLINE UINT16 GetThumb2Imm16(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ return ((p[0] << 12) & 0xf000) |
+ ((p[0] << 1) & 0x0800) |
+ ((p[1] >> 4) & 0x0700) |
+ ((p[1] >> 0) & 0x00ff);
+}
+
+//*****************************************************************************
+// Extract the 32-bit immediate from movw/movt sequence
+//*****************************************************************************
+UINT32 GetThumb2Mov32(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Make sure we are decoding movw/movt sequence
+ _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
+ _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
+
+ return (UINT32)GetThumb2Imm16(p) + ((UINT32)GetThumb2Imm16(p + 2) << 16);
+}
+
+//*****************************************************************************
+// Deposit the 16-bit immediate into ARM Thumb2 Instruction (format T2_N)
+//*****************************************************************************
+static FORCEINLINE void PutThumb2Imm16(UINT16 * p, UINT16 imm16)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+ Opcode0 &= ~((0xf000 >> 12) | (0x0800 >> 1));
+ Opcode1 &= ~((0x0700 << 4) | (0x00ff << 0));
+ Opcode0 |= (imm16 & 0xf000) >> 12;
+ Opcode0 |= (imm16 & 0x0800) >> 1;
+ Opcode1 |= (imm16 & 0x0700) << 4;
+ Opcode1 |= (imm16 & 0x00ff) << 0;
+ p[0] = Opcode0;
+ p[1] = Opcode1;
+}
+
+//*****************************************************************************
+// Deposit the 32-bit immediate into movw/movt Thumb2 sequence
+//*****************************************************************************
+void PutThumb2Mov32(UINT16 * p, UINT32 imm32)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Make sure we are decoding movw/movt sequence
+ _ASSERTE_IMPL((*(p+0) & 0xFBF0) == 0xF240);
+ _ASSERTE_IMPL((*(p+2) & 0xFBF0) == 0xF2C0);
+
+ PutThumb2Imm16(p, (UINT16)imm32);
+ PutThumb2Imm16(p + 2, (UINT16)(imm32 >> 16));
+}
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+INT32 GetThumb2BlRel24(UINT16 * p)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+
+ UINT32 S = Opcode0 >> 10;
+ UINT32 J2 = Opcode1 >> 11;
+ UINT32 J1 = Opcode1 >> 13;
+
+ INT32 ret =
+ ((S << 24) & 0x1000000) |
+ (((J1 ^ S ^ 1) << 23) & 0x0800000) |
+ (((J2 ^ S ^ 1) << 22) & 0x0400000) |
+ ((Opcode0 << 12) & 0x03FF000) |
+ ((Opcode1 << 1) & 0x0000FFE);
+
+ // Sign-extend and return
+ return (ret << 7) >> 7;
+}
+
+//*****************************************************************************
+// Extract the 24-bit rel offset from bl instruction
+//*****************************************************************************
+void PutThumb2BlRel24(UINT16 * p, INT32 imm24)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInThumb2BlRel24(imm24));
+
+#if defined(TARGET_ARM)
+ // Ensure that the ThumbBit is not set on the offset
+ // as it cannot be encoded.
+ _ASSERTE(!(imm24 & THUMB_CODE));
+#endif // TARGET_ARM
+
+ USHORT Opcode0 = p[0];
+ USHORT Opcode1 = p[1];
+ Opcode0 &= 0xF800;
+ Opcode1 &= 0xD000;
+
+ UINT32 S = (imm24 & 0x1000000) >> 24;
+ UINT32 J1 = ((imm24 & 0x0800000) >> 23) ^ S ^ 1;
+ UINT32 J2 = ((imm24 & 0x0400000) >> 22) ^ S ^ 1;
+
+ Opcode0 |= ((imm24 & 0x03FF000) >> 12) | (S << 10);
+ Opcode1 |= ((imm24 & 0x0000FFE) >> 1) | (J1 << 13) | (J2 << 11);
+
+ p[0] = Opcode0;
+ p[1] = Opcode1;
+
+ _ASSERTE(GetThumb2BlRel24(p) == imm24);
+}
+
+//*****************************************************************************
+// Extract the PC-Relative offset from a b or bl instruction
+//*****************************************************************************
+INT32 GetArm64Rel28(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 branchInstr = *pCode;
+
+ // first shift 6 bits left to set the sign bit,
+ // then arithmetic shift right by 4 bits
+ INT32 imm28 = (((INT32)(branchInstr & 0x03FFFFFF)) << 6) >> 4;
+
+ return imm28;
+}
+
+//*****************************************************************************
+// Extract the PC-Relative offset from an adrp instruction
+//*****************************************************************************
+INT32 GetArm64Rel21(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 addInstr = *pCode;
+
+ // 23-5 bits for the high part. Shift it by 5.
+ INT32 immhi = (((INT32)(addInstr & 0xFFFFE0))) >> 5;
+ // 30,29 bits for the lower part. Shift it by 29.
+ INT32 immlo = ((INT32)(addInstr & 0x60000000)) >> 29;
+
+ // Merge them
+ INT32 imm21 = (immhi << 2) | immlo;
+
+ return imm21;
+}
+
+//*****************************************************************************
+// Extract the PC-Relative offset from an add instruction
+//*****************************************************************************
+INT32 GetArm64Rel12(UINT32 * pCode)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ UINT32 addInstr = *pCode;
+
+ // 21-10 contains value. Mask 12 bits and shift by 10 bits.
+ INT32 imm12 = (INT32)(addInstr & 0x003FFC00) >> 10;
+
+ return imm12;
+}
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm28' into a b or bl instruction
+//*****************************************************************************
+void PutArm64Rel28(UINT32 * pCode, INT32 imm28)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel28(imm28));
+ _ASSERTE((imm28 & 0x3) == 0); // the low two bits must be zero
+
+ UINT32 branchInstr = *pCode;
+
+ branchInstr &= 0xFC000000; // keep bits 31-26
+
+ // Assemble the pc-relative delta 'imm28' into the branch instruction
+ branchInstr |= ((imm28 >> 2) & 0x03FFFFFF);
+
+ *pCode = branchInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel28(pCode) == imm28);
+}
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm21' into an adrp instruction
+//*****************************************************************************
+void PutArm64Rel21(UINT32 * pCode, INT32 imm21)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel21(imm21));
+
+ UINT32 adrpInstr = *pCode;
+ // Check adrp opcode 1ii1 0000 ...
+ _ASSERTE((adrpInstr & 0x9F000000) == 0x90000000);
+
+ adrpInstr &= 0x9F00001F; // keep bits 31, 28-24, 4-0.
+ INT32 immlo = imm21 & 0x03; // Extract low 2 bits which will occupy 30-29 bits.
+ INT32 immhi = (imm21 & 0x1FFFFC) >> 2; // Extract high 19 bits which will occupy 23-5 bits.
+ adrpInstr |= ((immlo << 29) | (immhi << 5));
+
+ *pCode = adrpInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel21(pCode) == imm21);
+}
+
+//*****************************************************************************
+// Deposit the PC-Relative offset 'imm12' into an add instruction
+//*****************************************************************************
+void PutArm64Rel12(UINT32 * pCode, INT32 imm12)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ // Verify that we got a valid offset
+ _ASSERTE(FitsInRel12(imm12));
+
+ UINT32 addInstr = *pCode;
+ // Check add opcode 1001 0001 00...
+ _ASSERTE((addInstr & 0xFFC00000) == 0x91000000);
+
+ addInstr &= 0xFFC003FF; // keep bits 31-22, 9-0
+ addInstr |= (imm12 << 10); // Occupy 21-10.
+
+ *pCode = addInstr; // write the assembled instruction
+
+ _ASSERTE(GetArm64Rel12(pCode) == imm12);
+}
+
+//---------------------------------------------------------------------
+// Splits a command line into argc/argv lists, using the VC7 parsing rules.
+//
+// This functions interface mimics the CommandLineToArgvW api.
+//
+// If function fails, returns NULL.
+//
+// If function suceeds, call delete [] on return pointer when done.
+//
+//---------------------------------------------------------------------
+// NOTE: Implementation-wise, once every few years it would be a good idea to
+// compare this code with the C runtime library's parse_cmdline method,
+// which is in vctools\crt\crtw32\startup\stdargv.c. (Note we don't
+// support wild cards, and we use Unicode characters exclusively.)
+// We are up to date as of ~6/2005.
+//---------------------------------------------------------------------
+LPWSTR *SegmentCommandLine(LPCWSTR lpCmdLine, DWORD *pNumArgs)
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_FAULT;
+
+
+ *pNumArgs = 0;
+
+ int nch = (int)wcslen(lpCmdLine);
+
+ // Calculate the worstcase storage requirement. (One pointer for
+ // each argument, plus storage for the arguments themselves.)
+ int cbAlloc = (nch+1)*sizeof(LPWSTR) + sizeof(WCHAR)*(nch + 1);
+ LPWSTR pAlloc = new (nothrow) WCHAR[cbAlloc / sizeof(WCHAR)];
+ if (!pAlloc)
+ return NULL;
+
+ LPWSTR *argv = (LPWSTR*) pAlloc; // We store the argv pointers in the first halt
+ LPWSTR pdst = (LPWSTR)( ((BYTE*)pAlloc) + sizeof(LPWSTR)*(nch+1) ); // A running pointer to second half to store arguments
+ LPCWSTR psrc = lpCmdLine;
+ WCHAR c;
+ BOOL inquote;
+ BOOL copychar;
+ int numslash;
+
+ // First, parse the program name (argv[0]). Argv[0] is parsed under
+ // special rules. Anything up to the first whitespace outside a quoted
+ // subtring is accepted. Backslashes are treated as normal characters.
+ argv[ (*pNumArgs)++ ] = pdst;
+ inquote = FALSE;
+ do {
+ if (*psrc == W('"') )
+ {
+ inquote = !inquote;
+ c = *psrc++;
+ continue;
+ }
+ *pdst++ = *psrc;
+
+ c = *psrc++;
+
+ } while ( (c != W('\0') && (inquote || (c != W(' ') && c != W('\t')))) );
+
+ if ( c == W('\0') ) {
+ psrc--;
+ } else {
+ *(pdst-1) = W('\0');
+ }
+
+ inquote = FALSE;
+
+
+
+ /* loop on each argument */
+ for(;;)
+ {
+ if ( *psrc )
+ {
+ while (*psrc == W(' ') || *psrc == W('\t'))
+ {
+ ++psrc;
+ }
+ }
+
+ if (*psrc == W('\0'))
+ break; /* end of args */
+
+ /* scan an argument */
+ argv[ (*pNumArgs)++ ] = pdst;
+
+ /* loop through scanning one argument */
+ for (;;)
+ {
+ copychar = 1;
+ /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
+ 2N+1 backslashes + " ==> N backslashes + literal "
+ N backslashes ==> N backslashes */
+ numslash = 0;
+ while (*psrc == W('\\'))
+ {
+ /* count number of backslashes for use below */
+ ++psrc;
+ ++numslash;
+ }
+ if (*psrc == W('"'))
+ {
+ /* if 2N backslashes before, start/end quote, otherwise
+ copy literally */
+ if (numslash % 2 == 0)
+ {
+ if (inquote && psrc[1] == W('"'))
+ {
+ psrc++; /* Double quote inside quoted string */
+ }
+ else
+ {
+ /* skip first quote char and copy second */
+ copychar = 0; /* don't copy quote */
+ inquote = !inquote;
+ }
+ }
+ numslash /= 2; /* divide numslash by two */
+ }
+
+ /* copy slashes */
+ while (numslash--)
+ {
+ *pdst++ = W('\\');
+ }
+
+ /* if at end of arg, break loop */
+ if (*psrc == W('\0') || (!inquote && (*psrc == W(' ') || *psrc == W('\t'))))
+ break;
+
+ /* copy character into argument */
+ if (copychar)
+ {
+ *pdst++ = *psrc;
+ }
+ ++psrc;
+ }
+
+ /* null-terminate the argument */
+
+ *pdst++ = W('\0'); /* terminate string */
+ }
+
+ /* We put one last argument in -- a null ptr */
+ argv[ (*pNumArgs) ] = NULL;
+
+ // If we hit this assert, we overwrote our destination buffer.
+ // Since we're supposed to allocate for the worst
+ // case, either the parsing rules have changed or our worse case
+ // formula is wrong.
+ _ASSERTE((BYTE*)pdst <= (BYTE*)pAlloc + cbAlloc);
+ return argv;
+}
+
+//======================================================================
+// This function returns true, if it can determine that the instruction pointer
+// refers to a code address that belongs in the range of the given image.
+BOOL IsIPInModule(PTR_VOID pModuleBaseAddress, PCODE ip)
+{
+ STATIC_CONTRACT_LEAF;
+ SUPPORTS_DAC;
+
+ struct Param
+ {
+ PTR_VOID pModuleBaseAddress;
+ PCODE ip;
+ BOOL fRet;
+ } param;
+ param.pModuleBaseAddress = pModuleBaseAddress;
+ param.ip = ip;
+ param.fRet = FALSE;
+
+// UNIXTODO: implement a proper version for PAL
+#ifdef HOST_WINDOWS
+ PAL_TRY(Param *, pParam, &param)
+ {
+ PTR_BYTE pBase = dac_cast<PTR_BYTE>(pParam->pModuleBaseAddress);
+
+ PTR_IMAGE_DOS_HEADER pDOS = NULL;
+ PTR_IMAGE_NT_HEADERS pNT = NULL;
+ USHORT cbOptHdr;
+ PCODE baseAddr;
+
+ //
+ // First, must validate the format of the PE headers to make sure that
+ // the fields we're interested in using exist in the image.
+ //
+
+ // Validate the DOS header.
+ pDOS = PTR_IMAGE_DOS_HEADER(pBase);
+ if (pDOS->e_magic != VAL16(IMAGE_DOS_SIGNATURE) ||
+ pDOS->e_lfanew == 0)
+ {
+ goto lDone;
+ }
+
+ // Validate the NT header
+ pNT = PTR_IMAGE_NT_HEADERS(pBase + VAL32(pDOS->e_lfanew));
+
+ if (pNT->Signature != VAL32(IMAGE_NT_SIGNATURE))
+ {
+ goto lDone;
+ }
+
+ // Validate that the optional header is large enough to contain the fields
+ // we're interested, namely IMAGE_OPTIONAL_HEADER::SizeOfImage. The reason
+ // we don't just check that SizeOfOptionalHeader == IMAGE_SIZEOF_NT_OPTIONAL_HEADER
+ // is due to VSW443590, which states that the extensibility of this structure
+ // is such that it is possible to include only a portion of the optional header.
+ cbOptHdr = pNT->FileHeader.SizeOfOptionalHeader;
+
+ // Check that the magic field is contained by the optional header and set to the correct value.
+ if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, Magic) + sizeofmember(IMAGE_OPTIONAL_HEADER, Magic)) ||
+ pNT->OptionalHeader.Magic != VAL16(IMAGE_NT_OPTIONAL_HDR_MAGIC))
+ {
+ goto lDone;
+ }
+
+ // Check that the SizeOfImage is contained by the optional header.
+ if (cbOptHdr < (offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage) + sizeofmember(IMAGE_OPTIONAL_HEADER, SizeOfImage)))
+ {
+ goto lDone;
+ }
+
+ //
+ // The real check
+ //
+
+ baseAddr = dac_cast<PCODE>(pBase);
+ if ((pParam->ip < baseAddr) || (pParam->ip >= (baseAddr + VAL32(pNT->OptionalHeader.SizeOfImage))))
+ {
+ goto lDone;
+ }
+
+ pParam->fRet = TRUE;
+
+lDone: ;
+ }
+ PAL_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
+ {
+ }
+ PAL_ENDTRY
+#endif // HOST_WINDOWS
+
+ return param.fRet;
+}
+
+namespace Clr
+{
+namespace Util
+{
+#ifdef HOST_WINDOWS
+ // Struct used to scope suspension of client impersonation for the current thread.
+ // https://docs.microsoft.com/en-us/windows/desktop/secauthz/client-impersonation
+ class SuspendImpersonation
+ {
+ public:
+ SuspendImpersonation()
+ : _token(nullptr)
+ {
+ // The approach used here matches what is used elsewhere in CLR (RevertIfImpersonated).
+ // In general, OpenThreadToken fails with ERROR_NO_TOKEN if impersonation is not active,
+ // fails with ERROR_CANT_OPEN_ANONYMOUS if anonymous impersonation is active, and otherwise
+ // succeeds and returns the active impersonation token.
+ BOOL res = ::OpenThreadToken(::GetCurrentThread(), TOKEN_IMPERSONATE, /* OpenAsSelf */ TRUE, &_token);
+ if (res != FALSE)
+ {
+ ::RevertToSelf();
+ }
+ else
+ {
+ _token = nullptr;
+ }
+ }
+
+ ~SuspendImpersonation()
+ {
+ if (_token != nullptr)
+ ::SetThreadToken(nullptr, _token);
+ }
+
+ private:
+ HandleHolder _token;
+ };
+
+ struct ProcessIntegrityResult
+ {
+ BOOL Success;
+ DWORD Integrity;
+ HRESULT LastError;
+
+ HRESULT RecordAndReturnError(HRESULT hr)
+ {
+ LastError = hr;
+ return hr;
+ }
+ };
+
+ // The system calls in this code can fail if run with reduced privileges.
+ // It is the caller's responsibility to choose an appropriate default in the event
+ // that this function fails to retrieve the current process integrity.
+ HRESULT GetCurrentProcessIntegrity(DWORD *integrity)
+ {
+ static ProcessIntegrityResult s_Result = { FALSE, 0, S_FALSE };
+
+ if (FALSE != InterlockedCompareExchangeT(&s_Result.Success, FALSE, FALSE))
+ {
+ *integrity = s_Result.Integrity;
+ return S_OK;
+ }
+
+ // Temporarily suspend impersonation (if possible) while computing the integrity level.
+ // If impersonation is active, the OpenProcessToken call below will check the impersonation
+ // token against the process token ACL, and will generally fail with ERROR_ACCESS_DENIED if
+ // the impersonation token is less privileged than this process's primary token.
+ Clr::Util::SuspendImpersonation si;
+
+ HandleHolder hToken;
+ if(!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
+ return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
+
+ DWORD dwSize = 0;
+ DWORD err = ERROR_SUCCESS;
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, nullptr, 0, &dwSize))
+ err = GetLastError();
+
+ // We need to make sure that GetTokenInformation failed in a predictable manner so we know that
+ // dwSize has the correct buffer size in it.
+ if (err != ERROR_INSUFFICIENT_BUFFER || dwSize == 0)
+ return s_Result.RecordAndReturnError((err == ERROR_SUCCESS) ? E_FAIL : HRESULT_FROM_WIN32(err));
+
+ NewArrayHolder<BYTE> pLabel = new (nothrow) BYTE[dwSize];
+ if (pLabel == NULL)
+ return s_Result.RecordAndReturnError(E_OUTOFMEMORY);
+
+ if(!GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)TokenIntegrityLevel, pLabel, dwSize, &dwSize))
+ return s_Result.RecordAndReturnError(HRESULT_FROM_GetLastError());
+
+ TOKEN_MANDATORY_LABEL *ptml = (TOKEN_MANDATORY_LABEL *)(void*)pLabel;
+ PSID psidIntegrityLevelLabel = ptml->Label.Sid;
+
+ s_Result.Integrity = *GetSidSubAuthority(psidIntegrityLevelLabel, (*GetSidSubAuthorityCount(psidIntegrityLevelLabel) - 1));
+ *integrity = s_Result.Integrity;
+ InterlockedExchangeT(&s_Result.Success, TRUE);
+ return S_OK;
+ }
+
+namespace Reg
+{
+ HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKeyName, LPCWSTR wszValueName, SString & ssValue)
+ {
+ STANDARD_VM_CONTRACT;
+
+ if (hKey == NULL)
+ {
+ return E_INVALIDARG;
+ }
+
+ RegKeyHolder hTargetKey;
+ if (wszSubKeyName == NULL || *wszSubKeyName == W('\0'))
+ { // No subkey was requested, use hKey as the resolved key.
+ hTargetKey = hKey;
+ hTargetKey.SuppressRelease();
+ }
+ else
+ { // Try to open the specified subkey.
+ if (WszRegOpenKeyEx(hKey, wszSubKeyName, 0, KEY_READ, &hTargetKey) != ERROR_SUCCESS)
+ return REGDB_E_CLASSNOTREG;
+ }
+
+ DWORD type;
+ DWORD size;
+ if ((WszRegQueryValueEx(hTargetKey, wszValueName, 0, &type, 0, &size) == ERROR_SUCCESS) &&
+ type == REG_SZ && size > 0)
+ {
+ LPWSTR wszValueBuf = ssValue.OpenUnicodeBuffer(static_cast<COUNT_T>((size / sizeof(WCHAR)) - 1));
+ LONG lResult = WszRegQueryValueEx(
+ hTargetKey,
+ wszValueName,
+ 0,
+ 0,
+ reinterpret_cast<LPBYTE>(wszValueBuf),
+ &size);
+
+ _ASSERTE(lResult == ERROR_SUCCESS);
+ if (lResult == ERROR_SUCCESS)
+ {
+ // Can't count on the returned size being accurate - I've seen at least
+ // one string with an extra NULL at the end that will cause the resulting
+ // SString to count the extra NULL as part of the string. An extra
+ // terminating NULL is not a legitimate scenario for REG_SZ - this must
+ // be done using REG_MULTI_SZ - however this was tolerated in the
+ // past and so it would be a breaking change to stop doing so.
+ _ASSERTE(wcslen(wszValueBuf) <= (size / sizeof(WCHAR)) - 1);
+ ssValue.CloseBuffer((COUNT_T)wcsnlen(wszValueBuf, (size_t)size));
+ }
+ else
+ {
+ ssValue.CloseBuffer(0);
+ return HRESULT_FROM_WIN32(lResult);
+ }
+
+ return S_OK;
+ }
+ else
+ {
+ return REGDB_E_KEYMISSING;
+ }
+ }
+
+ HRESULT ReadStringValue(HKEY hKey, LPCWSTR wszSubKey, LPCWSTR wszName, __deref_out __deref_out_z LPWSTR* pwszValue)
+ {
+ CONTRACTL {
+ NOTHROW;
+ GC_NOTRIGGER;
+ } CONTRACTL_END;
+
+ HRESULT hr = S_OK;
+ EX_TRY
+ {
+ StackSString ssValue;
+ if (SUCCEEDED(hr = ReadStringValue(hKey, wszSubKey, wszName, ssValue)))
+ {
+ *pwszValue = new WCHAR[ssValue.GetCount() + 1];
+ wcscpy_s(*pwszValue, ssValue.GetCount() + 1, ssValue.GetUnicode());
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+ return hr;
+ }
+} // namespace Reg
+
+namespace Com
+{
+ namespace __imp
+ {
+ __success(return == S_OK)
+ static
+ HRESULT FindSubKeyDefaultValueForCLSID(REFCLSID rclsid, LPCWSTR wszSubKeyName, SString & ssValue)
+ {
+ STANDARD_VM_CONTRACT;
+
+ WCHAR wszClsid[39];
+ if (GuidToLPWSTR(rclsid, wszClsid, NumItems(wszClsid)) == 0)
+ return E_UNEXPECTED;
+
+ StackSString ssKeyName;
+ ssKeyName.Append(SL(W("CLSID\\")));
+ ssKeyName.Append(wszClsid);
+ ssKeyName.Append(SL(W("\\")));
+ ssKeyName.Append(wszSubKeyName);
+
+ // Query HKCR first to retain backwards compat with previous implementation where HKCR was only queried.
+ // This is being done due to registry caching. This value will be used if the process integrity is medium or less.
+ HRESULT hkcrResult = Clr::Util::Reg::ReadStringValue(HKEY_CLASSES_ROOT, ssKeyName.GetUnicode(), nullptr, ssValue);
+
+ // HKCR is a virtualized registry hive that weaves together HKCU\Software\Classes and HKLM\Software\Classes
+ // Processes with high integrity or greater should only read from HKLM to avoid being hijacked by medium
+ // integrity processes writing to HKCU.
+ DWORD integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
+ HRESULT hr = Clr::Util::GetCurrentProcessIntegrity(&integrity);
+ if (hr != S_OK)
+ {
+ // In the event that we are unable to get the current process integrity,
+ // we assume that this process is running in an elevated state.
+ // GetCurrentProcessIntegrity may fail if the process has insufficient rights to get the integrity level
+ integrity = SECURITY_MANDATORY_PROTECTED_PROCESS_RID;
+ }
+
+ if (integrity > SECURITY_MANDATORY_MEDIUM_RID)
+ {
+ Clr::Util::SuspendImpersonation si;
+
+ // Clear the previous HKCR queried value
+ ssValue.Clear();
+
+ // Force to use HKLM
+ StackSString ssHklmKeyName(SL(W("SOFTWARE\\Classes\\")));
+ ssHklmKeyName.Append(ssKeyName);
+ return Clr::Util::Reg::ReadStringValue(HKEY_LOCAL_MACHINE, ssHklmKeyName.GetUnicode(), nullptr, ssValue);
+ }
+
+ return hkcrResult;
+ }
+ }
+
+ HRESULT FindInprocServer32UsingCLSID(REFCLSID rclsid, SString & ssInprocServer32Name)
+ {
+ WRAPPER_NO_CONTRACT;
+ return __imp::FindSubKeyDefaultValueForCLSID(rclsid, W("InprocServer32"), ssInprocServer32Name);
+ }
+} // namespace Com
+#endif // HOST_WINDOWS
+
+} // namespace Util
+} // namespace Clr
diff --git a/src/coreclr/utilcode/util_nodependencies.cpp b/src/coreclr/utilcode/util_nodependencies.cpp
new file mode 100644
index 00000000000..fb812b21ba6
--- /dev/null
+++ b/src/coreclr/utilcode/util_nodependencies.cpp
@@ -0,0 +1,814 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// Util_NoDependencies.cpp
+//
+
+//
+// This contains a bunch of C++ utility classes needed also for UtilCode without dependencies
+// (standalone version without CLR/clr.dll/mscoree.dll dependencies).
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+#include "utilcode.h"
+#include "ex.h"
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
+
+RunningOnStatusEnum gRunningOnStatus = RUNNING_ON_STATUS_UNINITED;
+
+#define NON_SUPPORTED_PLATFORM_MSGBOX_TITLE W("Platform not supported")
+#define NON_SUPPORTED_PLATFORM_MSGBOX_TEXT W("The minimum supported platform is Windows 7")
+#define NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE 0xBAD1BAD1
+
+//*****************************************************************************
+// One time initialization of the OS version
+//*****************************************************************************
+void InitRunningOnVersionStatus ()
+{
+#ifndef TARGET_UNIX
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ BOOL fSupportedPlatform = FALSE;
+ OSVERSIONINFOEX sVer;
+ DWORDLONG dwlConditionMask;
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 6;
+ sVer.dwMinorVersion = 2;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, VER_MAJORVERSION | VER_PLATFORMID | VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WIN8;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+
+ ZeroMemory(&sVer, sizeof(OSVERSIONINFOEX));
+ sVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ sVer.dwMajorVersion = 6;
+ sVer.dwMinorVersion = 1;
+ sVer.dwPlatformId = VER_PLATFORM_WIN32_NT;
+
+
+ dwlConditionMask = 0;
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ dwlConditionMask = VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ if(VerifyVersionInfo(&sVer, VER_MAJORVERSION | VER_PLATFORMID | VER_MINORVERSION, dwlConditionMask))
+ {
+ gRunningOnStatus = RUNNING_ON_WIN7;
+ fSupportedPlatform = TRUE;
+ goto CHECK_SUPPORTED;
+ }
+
+CHECK_SUPPORTED:
+
+ if (!fSupportedPlatform)
+ {
+ // The current platform isn't supported. Display a message box to this effect and exit.
+ // Note that this should never happen since the .NET Fx setup should not install on
+ // non supported platforms (which is why the message box text isn't localized).
+ UtilMessageBoxCatastrophicNonLocalized(NON_SUPPORTED_PLATFORM_MSGBOX_TEXT, NON_SUPPORTED_PLATFORM_MSGBOX_TITLE, MB_OK | MB_ICONERROR, TRUE);
+ TerminateProcess(GetCurrentProcess(), NON_SUPPORTED_PLATFORM_TERMINATE_ERROR_CODE);
+ }
+#endif // TARGET_UNIX
+} // InitRunningOnVersionStatus
+
+#ifndef HOST_64BIT
+//------------------------------------------------------------------------------
+// Returns TRUE if we are running on a 64-bit OS in WoW, FALSE otherwise.
+BOOL RunningInWow64()
+{
+ #ifdef TARGET_UNIX
+ return FALSE;
+ #else
+ static int s_Wow64Process;
+
+ if (s_Wow64Process == 0)
+ {
+ BOOL fWow64Process = FALSE;
+
+ if (!IsWow64Process(GetCurrentProcess(), &fWow64Process))
+ fWow64Process = FALSE;
+
+ s_Wow64Process = fWow64Process ? 1 : -1;
+ }
+
+ return (s_Wow64Process == 1) ? TRUE : FALSE;
+ #endif
+}
+#endif
+
+#ifndef TARGET_UNIX
+//------------------------------------------------------------------------------
+//
+// GetRegistryLongValue - Reads a configuration LONG value from the registry.
+//
+// Parameters
+// hKeyParent -- Parent key
+// szKey -- key to open
+// szName -- name of the value
+// pValue -- put value here, if found
+// fReadNonVirtualizedKey -- whether to read 64-bit hive on WOW64
+//
+// Returns
+// TRUE -- If the value was found and read
+// FALSE -- The value was not found, could not be read, or was not DWORD
+//
+// Exceptions
+// None
+//------------------------------------------------------------------------------
+BOOL GetRegistryLongValue(HKEY hKeyParent,
+ LPCWSTR szKey,
+ LPCWSTR szName,
+ long *pValue,
+ BOOL fReadNonVirtualizedKey)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ DWORD ret; // Return value from registry operation.
+ HKEYHolder hkey; // Registry key.
+ long iValue; // The value to read.
+ DWORD iType; // Type of value to get.
+ DWORD iSize; // Size of buffer.
+ REGSAM samDesired = KEY_READ; // Desired access rights to the key
+
+ if (fReadNonVirtualizedKey)
+ {
+ if (RunningInWow64())
+ {
+ samDesired |= KEY_WOW64_64KEY;
+ }
+ }
+
+ ret = WszRegOpenKeyEx(hKeyParent, szKey, 0, samDesired, &hkey);
+
+ // If we opened the key, see if there is a value.
+ if (ret == ERROR_SUCCESS)
+ {
+ iType = REG_DWORD;
+ iSize = sizeof(long);
+ ret = WszRegQueryValueEx(hkey, szName, NULL, &iType, reinterpret_cast<BYTE*>(&iValue), &iSize);
+
+ if (ret == ERROR_SUCCESS && iType == REG_DWORD && iSize == sizeof(long))
+ { // We successfully read a DWORD value.
+ *pValue = iValue;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+} // GetRegistryLongValue
+
+//----------------------------------------------------------------------------
+//
+// GetCurrentModuleFileName - Retrieve the current module's filename
+//
+// Arguments:
+// pBuffer - output string buffer
+//
+// Return Value:
+// S_OK on success, else detailed error code.
+//
+// Note:
+//
+//----------------------------------------------------------------------------
+HRESULT GetCurrentModuleFileName(SString& pBuffer)
+{
+ LIMITED_METHOD_CONTRACT;
+
+
+ DWORD ret = WszGetModuleFileName(NULL, pBuffer);
+
+ if (ret == 0)
+ {
+ return E_UNEXPECTED;
+ }
+
+
+ return S_OK;
+}
+
+//----------------------------------------------------------------------------
+//
+// IsCurrentModuleFileNameInAutoExclusionList - decide if the current module's filename
+// is in the AutoExclusionList list
+//
+// Arguments:
+// None
+//
+// Return Value:
+// TRUE or FALSE
+//
+// Note:
+// This function cannot be used in out of process scenarios like DAC because it
+// looks at current module's filename. In OOP we want to use target process's
+// module's filename.
+//
+//----------------------------------------------------------------------------
+BOOL IsCurrentModuleFileNameInAutoExclusionList()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ HKEYHolder hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug\\AutoExclusionList"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerAutoExclusionListKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ {
+ // there's not even an AutoExclusionList hive
+ return FALSE;
+ }
+
+ PathString wszAppName;
+
+ // Get the appname to look up in the exclusion or inclusion list.
+ if (GetCurrentModuleFileName(wszAppName) != S_OK)
+ {
+ // Assume it is not on the exclusion list if we cannot find the module's filename.
+ return FALSE;
+ }
+
+ // Look in AutoExclusionList key for appName get the size of any value stored there.
+ DWORD value, valueType, valueSize = sizeof(value);
+ ret = WszRegQueryValueEx(hKeyHolder, wszAppName, 0, &valueType, reinterpret_cast<BYTE*>(&value), &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_DWORD) && (value == 1))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+} // IsCurrentModuleFileNameInAutoExclusionList
+
+//*****************************************************************************
+// Retrieve information regarding what registered default debugger
+//*****************************************************************************
+void GetDebuggerSettingInfo(SString &ssDebuggerString, BOOL *pfAuto)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ EX_TRY
+ {
+ DWORD cchDebuggerString = MAX_LONGPATH;
+ INDEBUG(DWORD cchOldDebuggerString = cchDebuggerString);
+
+ WCHAR * buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
+ HRESULT hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
+ ssDebuggerString.CloseBuffer(cchDebuggerString);
+
+ while (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER))
+ {
+ _ASSERTE(cchDebuggerString > cchOldDebuggerString);
+ INDEBUG(cchOldDebuggerString = cchDebuggerString);
+
+ buf = ssDebuggerString.OpenUnicodeBuffer(cchDebuggerString);
+ hr = GetDebuggerSettingInfoWorker(buf, &cchDebuggerString, pfAuto);
+ ssDebuggerString.CloseBuffer(cchDebuggerString);
+ }
+
+ if (*ssDebuggerString.GetUnicode() == W('\0'))
+ {
+ ssDebuggerString.Clear();
+ }
+
+ if (FAILED(hr))
+ {
+ ssDebuggerString.Clear();
+ if (pfAuto)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ ssDebuggerString.Clear();
+ if (pfAuto)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+} // GetDebuggerSettingInfo
+
+//---------------------------------------------------------------------------------------
+//
+// GetDebuggerSettingInfoWorker - retrieve information regarding what registered default debugger
+//
+// Arguments:
+// * wszDebuggerString - [out] the string buffer to store the registered debugger launch
+// string
+// * pcchDebuggerString - [in, out] the size of string buffer in characters
+// * pfAuto - [in] the flag to indicate whether the debugger neeeds to be launched
+// automatically
+//
+// Return Value:
+// HRESULT indicating success or failure.
+//
+// Notes:
+// * wszDebuggerString can be NULL. When wszDebuggerString is NULL, pcchDebuggerString should
+// * point to a DWORD of zero. pcchDebuggerString cannot be NULL, and the DWORD pointed by
+// * pcchDebuggerString will store the used or required string buffer size in characters.
+HRESULT GetDebuggerSettingInfoWorker(__out_ecount_part_opt(*pcchDebuggerString, *pcchDebuggerString) LPWSTR wszDebuggerString, DWORD * pcchDebuggerString, BOOL * pfAuto)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ PRECONDITION(pcchDebuggerString != NULL);
+ }
+ CONTRACTL_END;
+
+ if ((pcchDebuggerString == NULL) || ((wszDebuggerString == NULL) && (*pcchDebuggerString != 0)))
+ {
+ return E_INVALIDARG;
+ }
+
+ // Initialize the output values before we start.
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = W('\0');
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+
+ HKEYHolder hKeyHolder;
+
+ // Look for "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"
+ DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, kUnmanagedDebuggerKey, 0, KEY_READ, &hKeyHolder);
+
+ if (ret != ERROR_SUCCESS)
+ { // Wow, there's not even an AeDebug hive, so no native debugger, no auto.
+ return S_OK;
+ }
+
+ // Look in AeDebug key for "Debugger"; get the size of any value stored there.
+ DWORD valueType, valueSize = 0;
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, 0, &valueType, 0, &valueSize);
+
+ _ASSERTE(pcchDebuggerString != NULL);
+ if ((wszDebuggerString == NULL) || (*pcchDebuggerString < valueSize / sizeof(WCHAR)))
+ {
+ *pcchDebuggerString = valueSize / sizeof(WCHAR) + 1;
+ return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
+ }
+
+ *pcchDebuggerString = valueSize / sizeof(WCHAR);
+
+ // The size of an empty string with the null terminator is 2.
+ BOOL fIsDebuggerStringEmptry = valueSize <= 2 ? TRUE : FALSE;
+
+ if ((ret != ERROR_SUCCESS) || (valueType != REG_SZ) || fIsDebuggerStringEmptry)
+ {
+ return S_OK;
+ }
+
+ _ASSERTE(wszDebuggerString != NULL);
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerValue, NULL, NULL, reinterpret_cast< LPBYTE >(wszDebuggerString), &valueSize);
+ if (ret != ERROR_SUCCESS)
+ {
+ *wszDebuggerString = W('\0');
+ return S_OK;
+ }
+
+ // The callers are in nothrow scope, so we must swallow exceptions and reset the output parameters to the
+ // default values if exceptions like OOM ever happen.
+ EX_TRY
+ {
+ if (pfAuto != NULL)
+ {
+ BOOL fAuto = FALSE;
+
+ // Get the appname to look up in DebugApplications key.
+ PathString wzAppName;
+ long iValue;
+
+ // Check DebugApplications setting
+ if ((SUCCEEDED(GetCurrentModuleFileName(wzAppName))) &&
+ (
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_LOCAL_MACHINE, kDebugApplicationsKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsPoliciesKey, wzAppName, &iValue, TRUE) ||
+ GetRegistryLongValue(HKEY_CURRENT_USER, kDebugApplicationsKey, wzAppName, &iValue, TRUE)
+ ) &&
+ (iValue == 1))
+ {
+ fAuto = TRUE;
+ }
+ else
+ {
+ // Look in AeDebug key for "Auto"; get the size of any value stored there.
+ ret = WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, 0, &valueType, 0, &valueSize);
+ if ((ret == ERROR_SUCCESS) && (valueType == REG_SZ) && (valueSize / sizeof(WCHAR) < MAX_LONGPATH))
+ {
+ WCHAR wzAutoKey[MAX_LONGPATH];
+ valueSize = NumItems(wzAutoKey) * sizeof(WCHAR);
+ WszRegQueryValueEx(hKeyHolder, kUnmanagedDebuggerAutoValue, NULL, NULL, reinterpret_cast< LPBYTE >(wzAutoKey), &valueSize);
+
+ // The OS's behavior is to consider Auto to be FALSE unless the first character is set
+ // to 1. They don't take into consideration the following characters. Also if the value
+ // isn't present they assume an Auto value of FALSE.
+ if ((wzAutoKey[0] == W('1')) && !IsCurrentModuleFileNameInAutoExclusionList())
+ {
+ fAuto = TRUE;
+ }
+ }
+ }
+
+ *pfAuto = fAuto;
+ }
+ }
+ EX_CATCH
+ {
+ if ((wszDebuggerString != NULL) && (*pcchDebuggerString > 0))
+ {
+ *wszDebuggerString = W('\0');
+ }
+
+ if (pfAuto != NULL)
+ {
+ *pfAuto = FALSE;
+ }
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return S_OK;
+} // GetDebuggerSettingInfoWorker
+#endif // TARGET_UNIX
+
+#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES) || defined(_DEBUG)
+
+//*****************************************************************************
+// Convert hex value into a wide string of hex digits
+//*****************************************************************************
+HRESULT GetStr(
+ DWORD hHexNum,
+ __out_ecount((cbHexNum * 2)) LPWSTR szHexNum,
+ DWORD cbHexNum)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE (szHexNum);
+ cbHexNum *= 2; // each nibble is a char
+ while (cbHexNum != 0)
+ {
+ DWORD thisHexDigit = hHexNum % 16;
+ hHexNum /= 16;
+ cbHexNum--;
+ if (thisHexDigit < 10)
+ {
+ *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit + W('0'));
+ }
+ else
+ {
+ *(szHexNum+cbHexNum) = (BYTE)(thisHexDigit - 10 + W('A'));
+ }
+ }
+ return S_OK;
+}
+
+//*****************************************************************************
+// Convert a GUID into a pointer to a Wide char string
+//*****************************************************************************
+int
+GuidToLPWSTR(
+ GUID Guid, // The GUID to convert.
+ __out_ecount(cchGuid) LPWSTR szGuid, // String into which the GUID is stored
+ DWORD cchGuid) // Count in wchars
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+
+ // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
+ // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
+
+ if (cchGuid < 39) // 38 chars + 1 null terminating.
+ return 0;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ szGuid[0] = W('{');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^
+ if (FAILED (GetStr(Guid.Data1, szGuid+1 , 4))) return 0;
+
+ szGuid[9] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetStr(Guid.Data2, szGuid+10, 2))) return 0;
+
+ szGuid[14] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetStr(Guid.Data3, szGuid+15, 2))) return 0;
+
+ szGuid[19] = W('-');
+
+ // Get the last two fields (which are byte arrays).
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ for (i=0; i < 2; ++i)
+ if (FAILED(GetStr(Guid.Data4[i], szGuid + 20 + (i * 2), 1)))
+ return (0);
+
+ szGuid[24] = W('-');
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^^^^^
+ for (i=0; i < 6; ++i)
+ if (FAILED(GetStr(Guid.Data4[i+2], szGuid + 25 + (i * 2), 1)))
+ return (0);
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ szGuid[37] = W('}');
+ szGuid[38] = W('\0');
+
+ return 39;
+} // GuidToLPWSTR
+
+//*****************************************************************************
+// Convert wide string of (at most eight) hex digits into a hex value
+//*****************************************************************************
+HRESULT GetHex(
+ DWORD * phHexNum,
+ __in_ecount((cbHexNum * 2)) LPCWSTR szHexNum,
+ DWORD cbHexNum)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE (szHexNum && phHexNum);
+ _ASSERTE(cbHexNum == 1 || cbHexNum == 2 || cbHexNum == 4);
+
+ cbHexNum *= 2; // each nibble is a char
+ DWORD val = 0;
+ for (DWORD i = 0; i < cbHexNum; ++i)
+ {
+ DWORD nibble = 0;
+ if (szHexNum[i] >= W('0') && szHexNum[i] <= W('9'))
+ {
+ nibble = szHexNum[i] - '0';
+ }
+ else if (szHexNum[i] >= W('A') && szHexNum[i] <= W('F'))
+ {
+ nibble = 10 + szHexNum[i] - 'A';
+ }
+ else if (szHexNum[i] >= W('a') && szHexNum[i] <= W('f'))
+ {
+ nibble = 10 + szHexNum[i] - 'a';
+ }
+ else
+ {
+ return E_FAIL;
+ }
+ val = (val << 4) + nibble;
+ }
+ *phHexNum = val;
+ return S_OK;
+}
+
+//*****************************************************************************
+// Parse a Wide char string into a GUID
+//*****************************************************************************
+BOOL
+LPWSTRToGuid(
+ GUID * Guid, // [OUT] The GUID to fill in
+ __in_ecount(cchGuid) LPCWSTR szGuid, // [IN] String to parse
+ DWORD cchGuid) // [IN] Count in wchars in string
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ int i;
+ DWORD dw;
+
+ // successive fields break the GUID into the form DWORD-WORD-WORD-WORD-WORD.DWORD
+ // covering the 128-bit GUID. The string includes enclosing braces, which are an OLE convention.
+
+ if (cchGuid < 38) // 38 chars + 1 null terminating.
+ return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ if (szGuid[0] != W('{')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^
+ if (FAILED (GetHex(&dw, szGuid+1 , 4))) return FALSE;
+ Guid->Data1 = dw;
+
+ if (szGuid[9] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetHex(&dw, szGuid+10, 2))) return FALSE;
+ Guid->Data2 = (WORD)dw;
+
+ if (szGuid[14] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ if (FAILED (GetHex(&dw, szGuid+15, 2))) return FALSE;
+ Guid->Data3 = (WORD)dw;
+
+ if (szGuid[19] != W('-')) return FALSE;
+
+ // Get the last two fields (which are byte arrays).
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^
+ for (i=0; i < 2; ++i)
+ {
+ if (FAILED(GetHex(&dw, szGuid + 20 + (i * 2), 1))) return FALSE;
+ Guid->Data4[i] = (BYTE)dw;
+ }
+
+ if (szGuid[24] != W('-')) return FALSE;
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^^^^^^^^^^^^
+ for (i=0; i < 6; ++i)
+ {
+ if (FAILED(GetHex(&dw, szGuid + 25 + (i * 2), 1))) return FALSE;
+ Guid->Data4[i+2] = (BYTE)dw;
+ }
+
+ // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
+ // ^
+ if (szGuid[37] != W('}')) return FALSE;
+
+ return TRUE;
+} // GuidToLPWSTR
+
+
+#ifdef _DEBUG
+// Always write regardless of registry.
+void _cdecl DbgWriteEx(LPCTSTR szFmt, ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ WCHAR rcBuff[1024];
+ va_list marker;
+
+ va_start(marker, szFmt);
+ _vsnwprintf_s(rcBuff, _countof(rcBuff), _TRUNCATE, szFmt, marker);
+ va_end(marker);
+ WszOutputDebugString(rcBuff);
+}
+#endif //_DEBUG
+
+/**************************************************************************/
+void ConfigDWORD::init(const CLRConfig::ConfigDWORDInfo & info)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ // make sure that the memory was zero initialized
+ _ASSERTE(m_inited == 0 || m_inited == 1);
+
+ m_value = CLRConfig::GetConfigValue(info);
+ m_inited = 1;
+}
+
+//---------------------------------------------------------------------------------------
+//
+// Takes a const input string, and returns the start & size of the substring that has all
+// leading and trailing whitespace removed. The original string is not modified.
+//
+// Arguments:
+// * pwsz - [in] points to const string we want to trim; [out] points to beginning
+// of trimmed substring of input string
+// * pcch - [in] Points to length in chars of input string (not counting null
+// terminator); [out] Points to length in chars of trimmed substring (not
+// counting null terminator)
+//
+void TrimWhiteSpace(__deref_inout_ecount(*pcch) LPCWSTR *pwsz, __inout LPDWORD pcch)
+{
+ LIMITED_METHOD_DAC_CONTRACT;
+
+ _ASSERTE (pwsz != NULL);
+ _ASSERTE (*pwsz != NULL);
+ _ASSERTE (pcch != NULL);
+
+ DWORD cch = *pcch;
+ LPCWSTR wszBeginning = *pwsz;
+ LPCWSTR wszEnd = wszBeginning + (cch - 1);
+
+ while ((cch != 0) && iswspace(*wszBeginning))
+ {
+ wszBeginning++;
+ cch--;
+ }
+
+ while ((cch != 0) && iswspace(*wszEnd))
+ {
+ wszEnd--;
+ cch--;
+ }
+
+ *pwsz = wszBeginning;
+ *pcch = cch;
+} // TrimWhiteSpace
+
+BOOL ThreadWillCreateGuardPage(SIZE_T sizeReservedStack, SIZE_T sizeCommitedStack)
+{
+ // We need to make sure there will be a reserved but never committed page at the end
+ // of the stack. We do here the check NT does when it creates the user stack to decide
+ // if there is going to be a guard page. However, that is not enough, as if we only
+ // have a guard page, we have nothing to protect us from going pass it. Well, in
+ // fact, there is something that we will protect you, there are certain places
+ // (RTLUnwind) in NT that will check that the current frame is within stack limits.
+ // If we are not it will bomb out. We will also bomb out if we touch the hard guard
+ // page.
+ //
+ // For situation B, teb->StackLimit is at the beggining of the user stack (ie
+ // before updating StackLimit it checks if it was able to create a new guard page,
+ // in this case, it can't), which makes the check fail in RtlUnwind.
+ //
+ // Situation A [ Hard guard page | Guard page | user stack]
+ //
+ // Situation B [ Guard page | User stack ]
+ //
+ // Situation C [ User stack ( no room for guard page) ]
+ //
+ // Situation D (W9x) : Guard page or not, w9x has a 64k reserved region below
+ // the stack, we don't need any checks at all
+ //
+ // We really want to be in situation A all the time, so we add one more page
+ // to our requirements (we require guard page + hard guard)
+
+ SYSTEM_INFO sysInfo;
+ ::GetSystemInfo(&sysInfo);
+
+ // OS rounds up sizes the following way to decide if it marks a guard page
+ sizeReservedStack = ALIGN(sizeReservedStack, ((size_t)sysInfo.dwAllocationGranularity)); // Allocation granularity
+ sizeCommitedStack = ALIGN(sizeCommitedStack, ((size_t)sysInfo.dwPageSize)); // Page Size
+
+ // OS wont create guard page, we can't execute managed code safely.
+ // We also have to make sure we have a 'hard' guard, thus we add another
+ // page to the memory we would need comitted.
+ // That is, the following code will check if sizeReservedStack is at least 2 pages
+ // more than sizeCommitedStack.
+ return (sizeReservedStack > sizeCommitedStack + ((size_t)sysInfo.dwPageSize));
+} // ThreadWillCreateGuardPage
+
diff --git a/src/coreclr/utilcode/utilmessagebox.cpp b/src/coreclr/utilcode/utilmessagebox.cpp
new file mode 100644
index 00000000000..5a8b1f5e2d6
--- /dev/null
+++ b/src/coreclr/utilcode/utilmessagebox.cpp
@@ -0,0 +1,412 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// UtilMessageBox.cpp
+//
+
+//
+// This module contains the message box utility code for the CLR. It is used
+// by code in the CLR itself as well as other tools that build in the CLR tree.
+// For message boxes inside the ExecutionEngine, EEMessageBox must be used
+// instead of the these APIs.
+//
+//*****************************************************************************
+#include "stdafx.h" // Standard header.
+#include <utilcode.h> // Utility helpers.
+#include <corerror.h>
+#include "clrversion.h"
+#include "../dlls/mscorrc/resource.h"
+#include "ex.h"
+#if !defined(FEATURE_CORESYSTEM)
+#undef NTDDI_VERSION
+#define NTDDI_VERSION NTDDI_WIN7
+#include "commctrl.h"
+#endif
+
+
+BOOL ShouldDisplayMsgBoxOnCriticalFailure()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ // To help find issues, we will always display dialogs for critical failures
+ // under debug builds. This includes asserts and other critical issues.
+ return TRUE;
+#else
+ // Retrieve error mode
+ UINT last = SetErrorMode(0);
+ SetErrorMode(last); //set back to previous value
+
+ // SEM_FAILCRITICALERRORS indicates that the system does not display the critical-error-handler
+ // message box. Instead, the system sends the error to the calling process.
+ return !(last & SEM_FAILCRITICALERRORS);
+#endif // _DEBUG
+}
+
+
+
+
+// We'd like to use TaskDialogIndirect for asserts coming from managed code in particular
+// to display the detailedText in a scrollable way. Also, we'd like to reuse the CLR's
+// plumbing code for the rest of parts of the assert dialog. Note that the simple
+// Win32 MessageBox does not support the detailedText value.
+// If we later refactor MessageBoxImpl into its own DLL, move the lines referencing
+// "Microsoft.Windows.Common-Controls" version 6 in stdafx.h as well.
+int MessageBoxImpl(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR message, // Message
+ LPCWSTR title, // Dialog box title
+ LPCWSTR detailedText, // Details like a stack trace, etc.
+ UINT uType)
+{
+ CONTRACTL
+ {
+ // May pump messages. Callers should be GC_TRIGGERS and MODE_PREEMPTIVE,
+ // but we can't include EE contracts here.
+ THROWS;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+ return WszMessageBox(hWnd, message, title, uType);
+}
+
+int UtilMessageBoxVA(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uTitle, // Resource Identifier for Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+ }
+ CONTRACTL_END;
+
+ SString text;
+ SString title;
+ int result = IDCANCEL;
+
+ EX_TRY
+ {
+ text.LoadResource(CCompRC::Error, uText);
+ title.LoadResource(CCompRC::Error, uTitle);
+
+ result = UtilMessageBoxNonLocalizedVA(hWnd, (LPWSTR)text.GetUnicode(),
+ (LPWSTR)title.GetUnicode(), uType, displayForNonInteractive, showFileNameInTitle, NULL, args);
+ }
+ EX_CATCH
+ {
+ result = IDCANCEL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+ return UtilMessageBoxNonLocalizedVA(hWnd, lpText, lpTitle, NULL, uType, displayForNonInteractive, showFileNameInTitle, pInputFromUser, args);
+}
+
+int UtilMessageBoxNonLocalizedVA(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title
+ LPCWSTR lpDetails,// Details like a stack trace, etc.
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ BOOL * pInputFromUser, // To distinguish between user pressing abort vs. assuming abort.
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ INJECT_FAULT(return IDCANCEL;);
+
+ // Assert if none of MB_ICON is set
+ PRECONDITION((uType & MB_ICONMASK) != 0);
+ }
+ CONTRACTL_END;
+
+ int result = IDCANCEL;
+ if (pInputFromUser != NULL)
+ {
+ *pInputFromUser = FALSE;
+ }
+
+ EX_TRY
+ {
+ StackSString formattedMessage;
+ StackSString formattedTitle;
+ SString details(lpDetails);
+ PathString fileName;
+ BOOL fDisplayMsgBox = TRUE;
+
+ // Format message string using optional parameters
+ formattedMessage.VPrintf(lpText, args);
+
+ // Try to get filename of Module and add it to title
+ if (showFileNameInTitle && WszGetModuleFileName(NULL, fileName))
+ {
+ LPCWSTR wszName = NULL;
+ size_t cchName = 0;
+
+
+
+ SplitPathInterior(fileName, NULL, NULL, NULL, NULL, &wszName, &cchName, NULL, NULL);
+ formattedTitle.Printf(W("%s - %s"), wszName, lpTitle);
+ }
+ else
+ {
+ formattedTitle.Set(lpTitle);
+ }
+
+#if !defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+ // If the current process isn't interactive (a service for example), then we report the message
+ // in the event log and via OutputDebugString.
+ //
+ // We may still however attempt to display the message box if the MB_SERVICE_NOTIFICATION
+ // message box style was specified.
+ if (!RunningInteractive())
+ {
+ StackSString message;
+
+ message.Printf(W(".NET Runtime version : %s - "), CLR_PRODUCT_VERSION_L);
+ if (lpTitle)
+ message.Append(lpTitle);
+ if (!formattedMessage.IsEmpty())
+ message.Append(formattedMessage);
+
+ ClrReportEvent(W(".NET Runtime"),
+ EVENTLOG_ERROR_TYPE, // event type
+ 0, // category zero
+ 1024, // event identifier
+ NULL, // no user security identifier
+ message.GetUnicode());
+
+ if(lpTitle != NULL)
+ WszOutputDebugString(lpTitle);
+ if(!formattedMessage.IsEmpty())
+ WszOutputDebugString(formattedMessage);
+
+ // If we are running as a service and displayForNonInteractive is FALSE then IDABORT is
+ // the best value to return as it will most likely cause callers of this API to abort the process.
+ // This is the right thing to do since attaching a debugger doesn't make much sense when the process isn't
+ // running in interactive mode.
+ if(!displayForNonInteractive)
+ {
+ fDisplayMsgBox = FALSE;
+ result = IDABORT;
+ }
+ else
+ {
+ // Include in the MB_DEFAULT_DESKTOP_ONLY style.
+ uType |= MB_DEFAULT_DESKTOP_ONLY;
+ }
+ }
+#endif //!defined(FEATURE_UTILCODE_NO_DEPENDENCIES)
+
+ if (fDisplayMsgBox)
+ {
+ // We normally want to set the reading direction (right-to-left etc.) based on the resources
+ // in use. However, outside the CLR (SELF_NO_HOST) we can't assume we have resources and
+ // in CORECLR we can't even necessarily expect that our CLR callbacks have been initialized.
+ // This code path is used for ASSERT dialogs.
+
+ result = MessageBoxImpl(hWnd, formattedMessage, formattedTitle, details, uType);
+
+ if (pInputFromUser != NULL)
+ {
+ *pInputFromUser = TRUE;
+ }
+ }
+ }
+ EX_CATCH
+ {
+ result = IDCANCEL;
+ }
+ EX_END_CATCH(SwallowAllExceptions);
+
+ return result;
+}
+
+int UtilMessageBox(
+ HWND hWnd, // Handle to Owner Window
+ UINT uText, // Resource Identifier for Text message
+ UINT uTitle, // Resource Identifier for Title
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxVA(hWnd, uText, uTitle, uType, displayForNonInteractive, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxNonLocalized(
+ HWND hWnd, // Handle to Owner Window
+ LPCWSTR lpText, // Text message
+ LPCWSTR lpTitle, // Title message
+ UINT uType, // Style of MessageBox
+ BOOL displayForNonInteractive, // Display even if the process is running non interactive
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ... ) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxNonLocalizedVA(
+ hWnd, lpText, lpTitle, uType, displayForNonInteractive, showFileNameInTitle, NULL, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophic(
+ UINT uText, // Text for MessageBox
+ UINT uTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxCatastrophicVA(uText, uTitle, uType, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophicNonLocalized(
+ LPCWSTR lpText, // Text for MessageBox
+ LPCWSTR lpTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ ...)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ va_list marker;
+ va_start(marker, showFileNameInTitle);
+
+ int result = UtilMessageBoxCatastrophicNonLocalizedVA(lpText, lpTitle, uType, showFileNameInTitle, marker);
+ va_end( marker );
+
+ return result;
+}
+
+int UtilMessageBoxCatastrophicVA(
+ UINT uText, // Text for MessageBox
+ UINT uTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HWND hwnd = NULL;
+
+ // We are already in a catastrophic situation so we can tolerate faults as well as GC mode violations to keep going.
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation);
+
+ if (!ShouldDisplayMsgBoxOnCriticalFailure())
+ return IDABORT;
+
+ // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
+ // owned by the current thread and should prevent interaction with them until dismissed.
+ uType |= MB_TASKMODAL;
+
+ return UtilMessageBoxVA(hwnd, uText, uTitle, uType, TRUE, showFileNameInTitle, args);
+}
+
+int UtilMessageBoxCatastrophicNonLocalizedVA(
+ LPCWSTR lpText, // Text for MessageBox
+ LPCWSTR lpTitle, // Title for MessageBox
+ UINT uType, // Style of MessageBox
+ BOOL showFileNameInTitle, // Flag to show FileName in Caption
+ va_list args) // Additional Arguments
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ }
+ CONTRACTL_END;
+
+ HWND hwnd = NULL;
+
+ // We are already in a catastrophic situation so we can tolerate faults as well as GC mode violations to keep going.
+ CONTRACT_VIOLATION(FaultNotFatal | GCViolation | ModeViolation);
+
+ if (!ShouldDisplayMsgBoxOnCriticalFailure())
+ return IDABORT;
+
+ // Add the MB_TASKMODAL style to indicate that the dialog should be displayed on top of the windows
+ // owned by the current thread and should prevent interaction with them until dismissed.
+ uType |= MB_TASKMODAL;
+
+ return UtilMessageBoxNonLocalizedVA(hwnd, lpText, lpTitle, uType, TRUE, showFileNameInTitle, NULL, args);
+}
+
diff --git a/src/coreclr/utilcode/utsem.cpp b/src/coreclr/utilcode/utsem.cpp
new file mode 100644
index 00000000000..66544c5db73
--- /dev/null
+++ b/src/coreclr/utilcode/utsem.cpp
@@ -0,0 +1,505 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+/******************************************************************************
+ FILE : UTSEM.CPP
+
+
+
+ Purpose: Part of the utilities library for the VIPER project
+
+ Abstract : Implements the UTSemReadWrite class.
+-------------------------------------------------------------------------------
+Revision History:
+
+
+*******************************************************************************/
+#include "stdafx.h"
+#include "clrhost.h"
+#include "ex.h"
+
+#include <utsem.h>
+#include "contract.h"
+
+// Consider replacing this with a #ifdef INTEROP_DEBUGGING
+#if !defined(SELF_NO_HOST) && defined(TARGET_X86) && !defined(TARGET_UNIX)
+// For Interop debugging, the UTSemReadWrite class must inform the debugger
+// that this thread can't be suspended currently. See vm\util.hpp for the
+// implementation of these methods.
+void IncCantStopCount();
+void DecCantStopCount();
+#else
+#define IncCantStopCount()
+#define DecCantStopCount()
+#endif // !SELF_NO_HOST && TARGET_X86
+
+/******************************************************************************
+Definitions of the bit fields in UTSemReadWrite::m_dwFlag:
+
+Warning: The code assume that READER_MASK is in the low-order bits of the DWORD.
+******************************************************************************/
+
+const ULONG READERS_MASK = 0x000003FF; // field that counts number of readers
+const ULONG READERS_INCR = 0x00000001; // amount to add to increment number of readers
+
+// The following field is 2 bits long to make it easier to catch errors.
+// (If the number of writers ever exceeds 1, we've got problems.)
+const ULONG WRITERS_MASK = 0x00000C00; // field that counts number of writers
+const ULONG WRITERS_INCR = 0x00000400; // amount to add to increment number of writers
+
+const ULONG READWAITERS_MASK = 0x003FF000; // field that counts number of threads waiting to read
+const ULONG READWAITERS_INCR = 0x00001000; // amount to add to increment number of read waiters
+
+const ULONG WRITEWAITERS_MASK = 0xFFC00000; // field that counts number of threads waiting to write
+const ULONG WRITEWAITERS_INCR = 0x00400000; // amount to add to increment number of write waiters
+
+// ======================================================================================
+// Spinning support
+
+// Copy of definition from file:..\VM\spinlock.h
+#define CALLER_LIMITS_SPINNING 0
+
+#if (defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)) || (defined(TARGET_UNIX) && defined(DACCESS_COMPILE))
+
+// When we do not have host, we just call OS - see file:..\VM\hosting.cpp#__SwitchToThread
+BOOL __SwitchToThread(DWORD dwSleepMSec, DWORD dwSwitchCount)
+{
+ // This is just simple implementation that does not support full dwSwitchCount arg
+ _ASSERTE(dwSwitchCount == CALLER_LIMITS_SPINNING);
+ return SwitchToThread();
+}
+
+Volatile<BOOL> g_fInitializedGlobalSystemInfo = FALSE;
+
+// Global System Information
+SYSTEM_INFO g_SystemInfo;
+
+// Configurable constants used across our spin locks
+SpinConstants g_SpinConstants = {
+ 50, // dwInitialDuration
+ 40000, // dwMaximumDuration - ideally (20000 * max(2, numProc)) ... updated in code:InitializeSpinConstants_NoHost
+ 3, // dwBackoffFactor
+ 10, // dwRepetitions
+ 0 // dwMonitorSpinCount
+};
+
+inline void InitializeSpinConstants_NoHost()
+{
+ g_SpinConstants.dwMaximumDuration = max(2, g_SystemInfo.dwNumberOfProcessors) * 20000;
+}
+
+#else //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+// Use VM/CrossGen functions and variables
+BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount);
+extern SYSTEM_INFO g_SystemInfo;
+extern SpinConstants g_SpinConstants;
+
+#endif //!SELF_NO_HOST || CROSSGEN_COMPILE
+
+/******************************************************************************
+Function : UTSemReadWrite::UTSemReadWrite
+
+Abstract: Constructor.
+******************************************************************************/
+UTSemReadWrite::UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+#if defined(SELF_NO_HOST) && !defined(CROSSGEN_COMPILE)
+ if (!g_fInitializedGlobalSystemInfo)
+ {
+ GetSystemInfo(&g_SystemInfo);
+ InitializeSpinConstants_NoHost();
+
+ g_fInitializedGlobalSystemInfo = TRUE;
+ }
+#endif //SELF_NO_HOST && !CROSSGEN_COMPILE
+
+ m_dwFlag = 0;
+ m_hReadWaiterSemaphore = NULL;
+ m_hWriteWaiterEvent = NULL;
+}
+
+
+/******************************************************************************
+Function : UTSemReadWrite::~UTSemReadWrite
+
+Abstract: Destructor
+******************************************************************************/
+UTSemReadWrite::~UTSemReadWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ _ASSERTE_MSG((m_dwFlag == (ULONG)0), "Destroying a UTSemReadWrite while in use");
+
+ if (m_hReadWaiterSemaphore != NULL)
+ CloseHandle(m_hReadWaiterSemaphore);
+
+ if (m_hWriteWaiterEvent != NULL)
+ CloseHandle(m_hWriteWaiterEvent);
+}
+
+//=======================================================================================
+//
+// Initialize the lock (its semaphore and event)
+//
+HRESULT
+UTSemReadWrite::Init()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+
+ _ASSERTE(m_hReadWaiterSemaphore == NULL);
+ _ASSERTE(m_hWriteWaiterEvent == NULL);
+
+ m_hReadWaiterSemaphore = WszCreateSemaphore(NULL, 0, MAXLONG, NULL);
+ IfNullRet(m_hReadWaiterSemaphore);
+
+ m_hWriteWaiterEvent = WszCreateEvent(NULL, FALSE, FALSE, NULL);
+ IfNullRet(m_hWriteWaiterEvent);
+
+ return S_OK;
+} // UTSemReadWrite::Init
+
+/******************************************************************************
+Function : UTSemReadWrite::LockRead
+
+Abstract: Obtain a shared lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ goto ReadLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ YieldProcessorNormalizedForPreSkylakeCount(i);
+
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag < READERS_MASK)
+ { // There are just readers in the play, try to add one more
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & READERS_MASK) == READERS_MASK)
+ { // The number of readers has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else if ((dwFlag & READWAITERS_MASK) == READWAITERS_MASK)
+ { // The number of readers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting reader and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + READWAITERS_INCR, dwFlag))
+ {
+ WaitForSingleObjectEx(m_hReadWaiterSemaphore, INFINITE, FALSE);
+ break;
+ }
+ }
+ }
+
+ReadLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero after acquiring read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockRead
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::LockWrite
+
+Abstract: Obtain an exclusive lock
+******************************************************************************/
+HRESULT UTSemReadWrite::LockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END;
+
+ // Inform CLR that the debugger shouldn't suspend this thread while
+ // holding this lock.
+ IncCantStopCount();
+
+ // First do some spinning - copied from file:..\VM\crst.cpp#CrstBase::SpinEnter
+ for (DWORD iter = 0; iter < g_SpinConstants.dwRepetitions; iter++)
+ {
+ DWORD i = g_SpinConstants.dwInitialDuration;
+
+ do
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ goto WriteLockAcquired;
+ }
+ }
+
+ if (g_SystemInfo.dwNumberOfProcessors <= 1)
+ { // We do not need to spin on a single processor
+ break;
+ }
+
+ // Delay by approximately 2*i clock cycles (Pentium III).
+ YieldProcessorNormalizedForPreSkylakeCount(i);
+
+ // exponential backoff: wait a factor longer in the next iteration
+ i *= g_SpinConstants.dwBackoffFactor;
+ } while (i < g_SpinConstants.dwMaximumDuration);
+
+ __SwitchToThread(0, CALLER_LIMITS_SPINNING);
+ }
+ // Stop spinning
+
+ // Start waiting
+ for (;;)
+ {
+ DWORD dwFlag = m_dwFlag;
+
+ if (dwFlag == 0)
+ { // No readers/writers in play, try to add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, WRITERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+ else if ((dwFlag & WRITEWAITERS_MASK) == WRITEWAITERS_MASK)
+ { // The number of writers waiting on semaphore has reached the maximum (0x3ff), wait 1s
+ ClrSleepEx(1000, FALSE);
+ }
+ else
+ { // Try to add waiting writer and then wait for signal
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag + WRITEWAITERS_INCR, dwFlag))
+ {
+ WaitForSingleObjectEx(m_hWriteWaiterEvent, INFINITE, FALSE);
+ break;
+ }
+ }
+
+ }
+
+WriteLockAcquired:
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero after acquiring write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 after acquiring write lock");
+ EE_LOCK_TAKEN(this);
+
+ return S_OK;
+} // UTSemReadWrite::LockWrite
+
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockRead
+
+Abstract: Release a shared lock
+******************************************************************************/
+void UTSemReadWrite::UnlockRead()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) != 0 && "reader count is zero before releasing read lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == 0 && "writer count is nonzero before releasing read lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == READERS_INCR)
+ { // we're the last reader, and nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READERS_MASK) > READERS_INCR)
+ { // we're not the last reader
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - READERS_INCR, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else
+ {
+ // here, there should be exactly 1 reader (us), and at least one waiting writer.
+ _ASSERTE ((dwFlag & READERS_MASK) == READERS_INCR && "UnlockRead consistency error 1");
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockRead consistency error 2");
+
+ // one or more writers is waiting, do one of them next
+ // (remove a reader (us), remove a write waiter, add a writer
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - READERS_INCR - WRITEWAITERS_INCR + WRITERS_INCR,
+ dwFlag))
+ {
+ SetEvent(m_hWriteWaiterEvent);
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockRead
+
+
+/******************************************************************************
+Function : UTSemReadWrite::UnlockWrite
+
+Abstract: Release an exclusive lock
+******************************************************************************/
+void UTSemReadWrite::UnlockWrite()
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ }
+ CONTRACTL_END;
+
+ ULONG dwFlag;
+ ULONG count;
+
+ _ASSERTE ((m_dwFlag & READERS_MASK) == 0 && "reader count is nonzero before releasing write lock");
+ _ASSERTE ((m_dwFlag & WRITERS_MASK) == WRITERS_INCR && "writer count is not 1 before releasing write lock");
+
+ for (;;)
+ {
+ dwFlag = m_dwFlag;
+
+ if (dwFlag == WRITERS_INCR)
+ { // nobody is waiting
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, (ULONG)0, dwFlag))
+ {
+ break;
+ }
+ }
+
+ else if ((dwFlag & READWAITERS_MASK) != 0)
+ { // one or more readers are waiting, do them all next
+ count = (dwFlag & READWAITERS_MASK) / READWAITERS_INCR;
+ // remove a writer (us), remove all read waiters, turn them into readers
+ if (dwFlag ==
+ InterlockedCompareExchangeT(
+ &m_dwFlag,
+ dwFlag - WRITERS_INCR - count * READWAITERS_INCR + count * READERS_INCR,
+ dwFlag))
+ {
+ ReleaseSemaphore(m_hReadWaiterSemaphore, count, NULL);
+ break;
+ }
+ }
+
+ else
+ { // one or more writers is waiting, do one of them next
+ _ASSERTE ((dwFlag & WRITEWAITERS_MASK) != 0 && "UnlockWrite consistency error");
+ // (remove a writer (us), remove a write waiter, add a writer
+ if (dwFlag == InterlockedCompareExchangeT (&m_dwFlag, dwFlag - WRITEWAITERS_INCR, dwFlag))
+ {
+ SetEvent(m_hWriteWaiterEvent);
+ break;
+ }
+ }
+ }
+
+ DecCantStopCount();
+ EE_LOCK_RELEASED(this);
+} // UTSemReadWrite::UnlockWrite
+
+#ifdef _DEBUG
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForRead()
+{
+ return ((m_dwFlag & READERS_MASK) != 0);
+}
+
+//=======================================================================================
+BOOL
+UTSemReadWrite::Debug_IsLockedForWrite()
+{
+ return ((m_dwFlag & WRITERS_MASK) != 0);
+}
+
+#endif //_DEBUG
+
diff --git a/src/coreclr/utilcode/winfix.cpp b/src/coreclr/utilcode/winfix.cpp
new file mode 100644
index 00000000000..acc91c6d5ba
--- /dev/null
+++ b/src/coreclr/utilcode/winfix.cpp
@@ -0,0 +1,300 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+//*****************************************************************************
+// WinWrap.cpp
+//
+
+//
+// This file contains wrapper functions for Win32 API's that take strings.
+//
+// COM+ internally uses UNICODE as the internal state and string format. This
+// file will undef the mapping macros so that one cannot mistakingly call a
+// method that isn't going to work. Instead, you have to call the correct
+// wrapper API.
+//
+//*****************************************************************************
+
+#include "stdafx.h" // Precompiled header key.
+#include "winwrap.h" // Header for macros and functions.
+#include "utilcode.h"
+#include "holder.h"
+#include "pedecoder.h"
+
+
+// ====== READ BEFORE ADDING CONTRACTS ==================================================
+// The functions in this file propagate SetLastError codes to their callers.
+// Contracts are not guaranteed to preserve these codes (and no, we're not taking
+// the overhead hit to make them do so. Don't bother asking.)
+//
+// Most of the wrappers have a contract of the form:
+//
+// NOTHROW;
+// INJECT_FAULT(xxx);
+//
+// For such functions, use the special purpose construct:
+//
+// WINWRAPPER_NO_CONTRACT(xxx);
+//
+// For everything else, use STATIC_CONTRACT.
+//
+#undef CONTRACT
+#define CONTRACT $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
+
+#undef CONTRACTL
+#define CONTRACTL $$$$$$$$READ_COMMENT_IN_WINFIX_CPP$$$$$$$$$$
+
+#ifdef ENABLE_CONTRACTS_IMPL
+static BOOL gWinWrapperContractRecursionBreak = FALSE;
+
+
+class WinWrapperContract
+{
+ public:
+ WinWrapperContract(const char *szFunction, const char *szFile, int lineNum)
+ {
+ CANNOT_HAVE_CONTRACT;
+
+ m_pClrDebugState = NULL;
+
+ if (gWinWrapperContractRecursionBreak)
+ {
+ return;
+ }
+
+ m_pClrDebugState = GetClrDebugState();
+
+ // Save old debug state
+ m_IncomingClrDebugState = *m_pClrDebugState;
+
+
+ m_pClrDebugState->ViolationMaskReset( ThrowsViolation );
+
+ if (m_pClrDebugState->IsFaultForbid() && !(m_pClrDebugState->ViolationMask() & (FaultViolation|FaultNotFatal|BadDebugState)))
+ {
+ gWinWrapperContractRecursionBreak = TRUE;
+
+ CONTRACT_ASSERT("INJECT_FAULT called in a FAULTFORBID region.",
+ Contract::FAULT_Forbid,
+ Contract::FAULT_Mask,
+ szFunction,
+ szFile,
+ lineNum
+ );
+ }
+
+
+ };
+
+ ~WinWrapperContract()
+ {
+ CANNOT_HAVE_CONTRACT;
+
+ //!!!!!! THIS DESTRUCTOR MUST NOT CHANGE THE GETLASTERROR VALUE !!!!!!
+
+ // Backout all changes to debug state.
+ if (m_pClrDebugState != NULL)
+ {
+ *m_pClrDebugState = m_IncomingClrDebugState;
+ }
+ }
+ private:
+ ClrDebugState *m_pClrDebugState;
+ ClrDebugState m_IncomingClrDebugState;
+
+};
+
+
+
+#endif
+
+#ifdef ENABLE_CONTRACTS_IMPL
+#define WINWRAPPER_NO_CONTRACT(stmt) \
+ STATIC_CONTRACT_NOTHROW; \
+ STATIC_CONTRACT_FAULT; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
+ WinWrapperContract __wcontract(__FUNCTION__, __FILE__, __LINE__); \
+ if (0) {stmt} \
+
+#define STATIC_WINWRAPPER_NO_CONTRACT(stmt) \
+ STATIC_CONTRACT_NOTHROW; \
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK; \
+ STATIC_CONTRACT_FAULT; \
+ if (0) {stmt} \
+
+
+#else
+#define WINWRAPPER_NO_CONTRACT(stmt)
+#define STATIC_WINWRAPPER_NO_CONTRACT(stmt)
+#endif
+
+ULONG g_dwMaxDBCSCharByteSize = 0;
+
+// The only purpose of this function is to make a local copy of lpCommandLine.
+// Because windows implementation of CreateProcessW can actually change lpCommandLine,
+// but we'd like to keep it const.
+BOOL
+WszCreateProcess(
+ LPCWSTR lpApplicationName,
+ LPCWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation
+ )
+{
+ WINWRAPPER_NO_CONTRACT(SetLastError(ERROR_OUTOFMEMORY); return 0;);
+
+ BOOL fResult;
+ DWORD err;
+ {
+ size_t commandLineLength = wcslen(lpCommandLine) + 1;
+ NewArrayHolder<WCHAR> nonConstCommandLine(new (nothrow) WCHAR[commandLineLength]);
+ if (nonConstCommandLine == NULL)
+ {
+ SetLastError(ERROR_OUTOFMEMORY);
+ return 0;
+ }
+
+ memcpy(nonConstCommandLine, lpCommandLine, commandLineLength * sizeof(WCHAR));
+
+ fResult = CreateProcessW(lpApplicationName,
+ nonConstCommandLine,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ (LPWSTR)lpCurrentDirectory,
+ lpStartupInfo,
+ lpProcessInformation);
+
+ // At the end of the current scope, the last error code will be overwritten by the destructor of
+ // NewArrayHolder. So we save the error code here, and restore it after the end of the current scope.
+ err = GetLastError();
+ }
+
+ SetLastError(err);
+ return fResult;
+}
+
+#ifndef TARGET_UNIX
+
+
+#include "psapi.h"
+#include "tlhelp32.h"
+#include "winnls.h"
+
+//********** Globals. *********************************************************
+bool g_fEnsureCharSetInfoInitialized = FALSE; // true if we've detected the platform's character set characteristics
+
+// Detect Unicode support of the operating system, and initialize globals
+void EnsureCharSetInfoInitialized()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+ STATIC_CONTRACT_CANNOT_TAKE_LOCK;
+
+ if (!g_fEnsureCharSetInfoInitialized)
+ {
+ // NOTE: Do not use any of the Wsz* wrapper functions right now. They will have
+ // problems.
+
+ // Per Shupak, you're supposed to get the maximum size of a DBCS char
+ // dynamically to work properly on all locales (bug 2757).
+ CPINFO cpInfo;
+ if (GetCPInfo(CP_ACP, &cpInfo))
+ g_dwMaxDBCSCharByteSize = cpInfo.MaxCharSize;
+ else
+ g_dwMaxDBCSCharByteSize = 2;
+
+ VolatileStore(&g_fEnsureCharSetInfoInitialized, true);
+ }
+
+ return;
+}
+
+
+// Running with an interactive workstation.
+BOOL RunningInteractive()
+{
+ STATIC_CONTRACT_NOTHROW;
+ STATIC_CONTRACT_FORBID_FAULT;
+
+ static int fInteractive = -1;
+ if (fInteractive != -1)
+ return fInteractive != 0;
+
+#if !defined(FEATURE_CORESYSTEM)
+ HWINSTA hwinsta = NULL;
+
+ if ((hwinsta = GetProcessWindowStation() ) != NULL)
+ {
+ DWORD lengthNeeded;
+ USEROBJECTFLAGS flags;
+
+ if (GetUserObjectInformationW (hwinsta, UOI_FLAGS, &flags, sizeof(flags), &lengthNeeded))
+ {
+ if ((flags.dwFlags & WSF_VISIBLE) == 0)
+ fInteractive = 0;
+ }
+ }
+#endif // !FEATURE_CORESYSTEM
+
+ if (fInteractive != 0)
+ fInteractive = 1;
+
+ return fInteractive != 0;
+}
+
+typedef HRESULT(WINAPI *pfnSetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription);
+extern pfnSetThreadDescription g_pfnSetThreadDescription;
+
+// Dummy method if windows version does not support it
+HRESULT SetThreadDescriptionDummy(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ return NOERROR;
+}
+
+HRESULT WINAPI InitializeSetThreadDescription(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll"));
+
+ pfnSetThreadDescription pLocal = NULL;
+ if (hKernel32 != NULL)
+ {
+ // store to thread local variable to prevent data race
+ pLocal = (pfnSetThreadDescription)GetProcAddress(hKernel32, "SetThreadDescription");
+ }
+
+ if (pLocal == NULL) // method is only available with Windows 10 Creators Update or later
+ {
+ g_pfnSetThreadDescription = SetThreadDescriptionDummy;
+ }
+ else
+ {
+ g_pfnSetThreadDescription = pLocal;
+ }
+
+ return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
+
+pfnSetThreadDescription g_pfnSetThreadDescription = &InitializeSetThreadDescription;
+
+// Set unmanaged thread name which will show up in ETW and Debuggers which know how to read this data.
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ return g_pfnSetThreadDescription(hThread, lpThreadDescription);
+}
+
+#else //!TARGET_UNIX
+
+HRESULT SetThreadName(HANDLE hThread, PCWSTR lpThreadDescription)
+{
+ return SetThreadDescription(hThread, lpThreadDescription);
+}
+
+#endif //!TARGET_UNIX
diff --git a/src/coreclr/utilcode/yieldprocessornormalized.cpp b/src/coreclr/utilcode/yieldprocessornormalized.cpp
new file mode 100644
index 00000000000..4242f82792b
--- /dev/null
+++ b/src/coreclr/utilcode/yieldprocessornormalized.cpp
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "stdafx.h"
+
+// Defaults are for when InitializeYieldProcessorNormalized has not yet been called or when no measurement is done, and are
+// tuned for Skylake processors
+unsigned int g_yieldsPerNormalizedYield = 1; // current value is for Skylake processors, this is expected to be ~8 for pre-Skylake
+unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration = 7;