diff options
Diffstat (limited to 'src')
36 files changed, 1740 insertions, 200 deletions
diff --git a/src/debug/ee/wks/wks.nativeproj b/src/debug/ee/wks/wks.nativeproj index 8d89ac45cc..304c591485 100644 --- a/src/debug/ee/wks/wks.nativeproj +++ b/src/debug/ee/wks/wks.nativeproj @@ -34,6 +34,7 @@ <PreprocessAssembleArm Condition="'$(BuildArchitecture)' == 'arm'" Include="..\arm\dbghelpers.asm" /> <PreprocessAssembleArm Condition="'$(BuildArchitecture)' == 'arm64'" Include="..\arm64\dbghelpers.asm" /> <AssembleArm Condition="'$(BuildArchitecture)' == 'arm'" Include="$(IntermediateOutputDirectory)\dbghelpers.i" /> + <AssembleArm64 Condition="'$(BuildArchitecture)' == 'arm64'" Include="$(IntermediateOutputDirectory)\dbghelpers.i" /> <Assemble386 Condition="'$(BuildArchitecture)' == 'i386'" Include="..\i386\dbghelpers.asm" /> <AssembleAmd64 Condition="'$(BuildArchitecture)' == 'amd64'" Include="..\amd64\dbghelpers.asm" /> </ItemGroup> diff --git a/src/dlls/mscoree/CMakeLists.txt b/src/dlls/mscoree/CMakeLists.txt index 7af76fbe6b..6a157e4105 100644 --- a/src/dlls/mscoree/CMakeLists.txt +++ b/src/dlls/mscoree/CMakeLists.txt @@ -1,5 +1,9 @@ include_directories("../../inc") +if(FEATURE_GDBJIT) + add_definitions(-DFEATURE_GDBJIT) +endif(FEATURE_GDBJIT) + set(CLR_SOURCES mscoree.cpp unixinterface.cpp diff --git a/src/dlls/mscoree/unixinterface.cpp b/src/dlls/mscoree/unixinterface.cpp index be345b3ca1..897924c90c 100644 --- a/src/dlls/mscoree/unixinterface.cpp +++ b/src/dlls/mscoree/unixinterface.cpp @@ -15,6 +15,9 @@ #include <utilcode.h> #include <corhost.h> #include <configuration.h> +#ifdef FEATURE_GDBJIT +#include "../../vm/gdbjithelpers.h" +#endif // FEATURE_GDBJIT typedef int (STDMETHODCALLTYPE *HostMain)( const int argc, @@ -137,6 +140,11 @@ static void ConvertConfigPropertiesToUnicode( extern "C" LPCWSTR g_CLRJITPath; #endif // !defined(FEATURE_MERGE_JIT_AND_ENGINE) +#ifdef FEATURE_GDBJIT +GetInfoForMethodDelegate getInfoForMethodDelegate = NULL; +extern "C" int coreclr_create_delegate(void*, unsigned int, const char*, const char*, const char*, void**); +#endif //FEATURE_GDBJIT + // // Initialize the CoreCLR. Creates and starts CoreCLR host and creates an app domain // @@ -238,8 +246,24 @@ int coreclr_initialize( { host.SuppressRelease(); *hostHandle = host; - } +#ifdef FEATURE_GDBJIT + hr = coreclr_create_delegate(*hostHandle, + *domainId, + "System.Diagnostics.Debug.SymbolReader", + "System.Diagnostics.Debug.SymbolReader.SymbolReader", + "GetInfoForMethod", + (void**)&getInfoForMethodDelegate); + + if (!SUCCEEDED(hr)) + { + fprintf(stderr, + "Can't create delegate for 'System.Diagnostics.Debug.SymbolReader.SymbolReader.GetInfoForMethod' " + "method - status: 0x%08x\n", hr); + } + hr = S_OK; // We don't need to fail if we can't create delegate +#endif + } return hr; } diff --git a/src/gc/env/gcenv.os.h b/src/gc/env/gcenv.os.h index 0cdc7a4d16..bb0153f117 100644 --- a/src/gc/env/gcenv.os.h +++ b/src/gc/env/gcenv.os.h @@ -240,9 +240,9 @@ public: // specified, it returns amount of actual physical memory. static uint64_t GetPhysicalMemoryLimit(); - // Get global memory status + // Get memory status // Parameters: - // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory + // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory // that is in use (0 indicates no memory use and 100 indicates full memory use). // available_physical - The amount of physical memory currently available, in bytes. // available_page_file - The maximum amount of memory the current process can commit, in bytes. diff --git a/src/gc/sample/gcenv.windows.cpp b/src/gc/sample/gcenv.windows.cpp index e35af6b6a0..76187f2185 100644 --- a/src/gc/sample/gcenv.windows.cpp +++ b/src/gc/sample/gcenv.windows.cpp @@ -13,8 +13,6 @@ #include "gcenv.h" #include "gc.h" -static LARGE_INTEGER performanceFrequency; - MethodTable * g_pFreeObjectMethodTable; int32_t g_TrapReturningThreads; @@ -23,12 +21,14 @@ bool g_fFinalizerRunOnShutDown; GCSystemInfo g_SystemInfo; +static LARGE_INTEGER g_performanceFrequency; + // Initialize the interface implementation // Return: // true if it has succeeded, false if it has failed bool GCToOSInterface::Initialize() { - if (!::QueryPerformanceFrequency(&performanceFrequency)) + if (!::QueryPerformanceFrequency(&g_performanceFrequency)) { return false; } @@ -310,9 +310,12 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit() return memStatus.ullTotalPhys; } -// Get global memory status +// Get memory status // Parameters: -// ms - pointer to the structure that will be filled in with the memory status +// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory +// that is in use (0 indicates no memory use and 100 indicates full memory use). +// available_physical - The amount of physical memory currently available, in bytes. +// available_page_file - The maximum amount of memory the current process can commit, in bytes. void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { MEMORYSTATUSEX memStatus; @@ -356,14 +359,7 @@ int64_t GCToOSInterface::QueryPerformanceCounter() // The counter frequency int64_t GCToOSInterface::QueryPerformanceFrequency() { - LARGE_INTEGER frequency; - if (!::QueryPerformanceFrequency(&frequency)) - { - _ASSERTE(!"Fatal Error - cannot query performance counter."); - abort(); - } - - return frequency.QuadPart; + return g_performanceFrequency.QuadPart; } // Get a time stamp with a low precision diff --git a/src/inc/corexcep.h b/src/inc/corexcep.h index d842545038..f1bed3e77b 100644 --- a/src/inc/corexcep.h +++ b/src/inc/corexcep.h @@ -14,7 +14,7 @@ // All COM+ exceptions are expressed as a RaiseException with this exception // code. If you change this value, you must also change -// bcl\src\system\Exception.cs's _COMPlusExceptionCode value. +// mscorlib\src\system\Exception.cs's _COMPlusExceptionCode value. #define EXCEPTION_MSVC 0xe06d7363 // 0xe0000000 | 'msc' diff --git a/src/jit/assertionprop.cpp b/src/jit/assertionprop.cpp index 1ac1cd285f..781e46068b 100644 --- a/src/jit/assertionprop.cpp +++ b/src/jit/assertionprop.cpp @@ -754,12 +754,13 @@ Compiler::AssertionDsc * Compiler::optGetAssertion(AssertionIndex assertIndex) { assert(NO_ASSERTION_INDEX == 0); noway_assert(assertIndex != NO_ASSERTION_INDEX); + noway_assert(assertIndex <= optAssertionCount); + AssertionDsc* assertion = &optAssertionTabPrivate[assertIndex - 1]; +#ifdef DEBUG + optDebugCheckAssertion(assertion); +#endif - if (assertIndex > optMaxAssertionCount) - { - return nullptr; - } - return &optAssertionTabPrivate[assertIndex - 1]; + return assertion; } /***************************************************************************** @@ -1488,6 +1489,63 @@ Compiler::AssertionIndex Compiler::optAddAssertion(AssertionDsc* newAssertion) } #ifdef DEBUG +void Compiler::optDebugCheckAssertion(AssertionDsc* assertion) +{ + assert(assertion->assertionKind < OAK_COUNT); + assert(assertion->op1.kind < O1K_COUNT); + assert(assertion->op2.kind < O2K_COUNT); + // It would be good to check that op1.vn and op2.vn are valid value numbers. + + switch(assertion->op1.kind) + { + case O1K_LCLVAR: + case O1K_EXACT_TYPE: + case O1K_SUBTYPE: + assert(assertion->op1.lcl.lclNum < lvaCount); + assert(optLocalAssertionProp || + ((assertion->op1.lcl.ssaNum - SsaConfig::UNINIT_SSA_NUM) < lvaTable[assertion->op1.lcl.lclNum].lvNumSsaNames)); + break; + case O1K_ARR_BND: + // It would be good to check that bnd.vnIdx and bnd.vnLen are valid value numbers. + break; + case O1K_ARRLEN_OPER_BND: + case O1K_ARRLEN_LOOP_BND: + case O1K_CONSTANT_LOOP_BND: + assert(!optLocalAssertionProp); + break; + default: + break; + } + switch (assertion->op2.kind) + { + case O2K_IND_CNS_INT: + case O2K_CONST_INT: + { + // The only flags that can be set are those in the GTF_ICON_HDL_MASK, or bit 0, which is + // used to indicate a long constant. + assert((assertion->op2.u1.iconFlags & ~(GTF_ICON_HDL_MASK|1)) == 0); + switch (assertion->op1.kind) + { + case O1K_EXACT_TYPE: + case O1K_SUBTYPE: + assert(assertion->op2.u1.iconFlags != 0); + break; + case O1K_LCLVAR: + case O1K_ARR_BND: + assert(lvaTable[assertion->op1.lcl.lclNum].lvType != TYP_REF || assertion->op2.u1.iconVal == 0); + break; + default: + break; + } + } + break; + + default: + // for all other 'assertion->op2.kind' values we don't check anything + break; + } +} + /***************************************************************************** * * Verify that assertion prop related assumptions are valid. If "index" @@ -1502,33 +1560,7 @@ void Compiler::optDebugCheckAssertions(AssertionIndex index) for (AssertionIndex ind = start; ind <= end; ++ind) { AssertionDsc* assertion = optGetAssertion(ind); - switch (assertion->op2.kind) - { - case O2K_IND_CNS_INT: - case O2K_CONST_INT: - { - switch (assertion->op1.kind) - { - case O1K_EXACT_TYPE: - case O1K_SUBTYPE: - assert(assertion->op2.u1.iconFlags != 0); - break; - case O1K_ARRLEN_OPER_BND: - case O1K_ARRLEN_LOOP_BND: - case O1K_CONSTANT_LOOP_BND: - assert(!optLocalAssertionProp); - break; - default: - assert(lvaTable[assertion->op1.lcl.lclNum].lvType != TYP_REF || assertion->op2.u1.iconVal == 0); - break; - } - } - break; - - default: - // for all other 'assertion->op2.kind' values we don't check anything - break; - } + optDebugCheckAssertion(assertion); } } #endif @@ -4282,6 +4314,18 @@ ASSERT_TP* Compiler::optInitAssertionDataflowFlags() { ASSERT_TP* jumpDestOut = fgAllocateTypeForEachBlk<ASSERT_TP>(); + // The local assertion gen phase may have created unreachable blocks. + // They will never be visited in the dataflow propagation phase, so they need to + // be initialized correctly. This means that instead of setting their sets to + // apFull (i.e. all possible bits set), we need to set the bits only for valid + // assertions (note that at this point we are not creating any new assertions). + // Also note that assertion indices start from 1. + ASSERT_TP apValidFull = optNewEmptyAssertSet(); + for (int i = 1; i <= optAssertionCount; i++) + { + BitVecOps::AddElemD(apTraits, apValidFull, i-1); + } + // Initially estimate the OUT sets to everything except killed expressions // Also set the IN sets to 1, so that we can perform the intersection. // Also, zero-out the flags for handler blocks, as we could be in the @@ -4290,11 +4334,16 @@ ASSERT_TP* Compiler::optInitAssertionDataflowFlags() // edges. for (BasicBlock* block = fgFirstBB; block; block = block->bbNext) { - block->bbAssertionIn = bbIsHandlerBeg(block) ? optNewEmptyAssertSet() : optNewFullAssertSet(); + block->bbAssertionIn = optNewEmptyAssertSet(); + if (!bbIsHandlerBeg(block)) + { + BitVecOps::Assign(apTraits, block->bbAssertionIn, apValidFull); + } block->bbAssertionGen = optNewEmptyAssertSet(); - block->bbAssertionOut = optNewFullAssertSet(); + block->bbAssertionOut = optNewEmptyAssertSet(); + BitVecOps::Assign(apTraits, block->bbAssertionOut, apValidFull); jumpDestOut[block->bbNum] = optNewEmptyAssertSet(); - BitVecOps::Assign(apTraits, jumpDestOut[block->bbNum], apFull); + BitVecOps::Assign(apTraits, jumpDestOut[block->bbNum], apValidFull); } // Compute the data flow values for all tracked expressions // IN and OUT never change for the initial basic block B1 @@ -4817,9 +4866,9 @@ void Compiler::optAssertionPropMain() // and thus we must morph, set order, re-link for (GenTreePtr tree = stmt->gtStmt.gtStmtList; tree; tree = tree->gtNext) { - JITDUMP("Propagating %s assertions for BB%02d, stmt %08X, tree %08X, tree -> %d\n", - BitVecOps::ToString(apTraits, assertions), - block->bbNum, dspPtr(stmt), dspPtr(tree), tree->GetAssertion()); + JITDUMP("Propagating %s assertions for BB%02d, stmt [%06d], tree [%06d], tree -> %d\n", + BitVecOps::ToString(apTraits, assertions), + block->bbNum, dspTreeID(stmt), dspTreeID(tree), tree->GetAssertion()); GenTreePtr newTree = optAssertionProp(assertions, tree, stmt); if (newTree) diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 3eb2fdcb83..e3a4b519c7 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -5560,7 +5560,8 @@ public: OAK_EQUAL, OAK_NOT_EQUAL, OAK_SUBRANGE, - OAK_NO_THROW }; + OAK_NO_THROW, + OAK_COUNT }; enum optOp1Kind { O1K_INVALID, O1K_LCLVAR, @@ -5569,7 +5570,8 @@ public: O1K_ARRLEN_LOOP_BND, O1K_CONSTANT_LOOP_BND, O1K_EXACT_TYPE, - O1K_SUBTYPE }; + O1K_SUBTYPE, + O1K_COUNT }; enum optOp2Kind { O2K_INVALID, O2K_LCLVAR_COPY, @@ -5578,7 +5580,8 @@ public: O2K_CONST_LONG, O2K_CONST_DOUBLE, O2K_ARR_LEN, - O2K_SUBRANGE }; + O2K_SUBRANGE, + O2K_COUNT }; struct AssertionDsc { optAssertionKind assertionKind; @@ -5754,6 +5757,10 @@ public: case O2K_INVALID: // we will return false break; + + default: + assert(!"Unexpected value for op2.kind in AssertionDsc."); + break; } return false; } @@ -5892,6 +5899,7 @@ public : #ifdef DEBUG void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex=0); + void optDebugCheckAssertion(AssertionDsc* assertion); void optDebugCheckAssertions(AssertionIndex AssertionIndex); #endif void optAddCopies(); diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp index b6127e72ed..1ff7b53348 100644 --- a/src/jit/compiler.hpp +++ b/src/jit/compiler.hpp @@ -3474,8 +3474,9 @@ void Compiler::optAssertionReset(AssertionIndex limit) while (optAssertionCount > limit) { - AssertionIndex index = optAssertionCount--; + AssertionIndex index = optAssertionCount; AssertionDsc* curAssertion = optGetAssertion(index); + optAssertionCount--; unsigned lclNum = curAssertion->op1.lcl.lclNum; assert(lclNum < lvaTableCnt); BitVecOps::RemoveElemD(apTraits, GetAssertionDep(lclNum), index - 1); diff --git a/src/jit/ee_il_dll.cpp b/src/jit/ee_il_dll.cpp index c726856b9b..ff42443c25 100755 --- a/src/jit/ee_il_dll.cpp +++ b/src/jit/ee_il_dll.cpp @@ -23,6 +23,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #if !defined(PLATFORM_UNIX) #include <io.h> // For _dup, _setmode #include <fcntl.h> // For _O_TEXT +#include <errno.h> // For EINVAL #endif /*****************************************************************************/ diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp index 98f8183b7a..aa63b8f812 100644 --- a/src/jit/importer.cpp +++ b/src/jit/importer.cpp @@ -6830,7 +6830,7 @@ var_types Compiler::impImportCall(OPCODE opcode, //------------------------------------------------------------------------- // The "this" pointer - if (!(mflags & CORINFO_FLG_STATIC) || opcode == CEE_NEWOBJ) + if (!(mflags & CORINFO_FLG_STATIC) && !((opcode == CEE_NEWOBJ) && (newobjThis == nullptr))) { GenTreePtr obj; @@ -11736,10 +11736,18 @@ DO_LDFTN: // At present this can only be String else if (clsFlags & CORINFO_FLG_VAROBJSIZE) { - // This is the case for variable-sized objects that are not - // arrays. In this case, call the constructor with a null 'this' - // pointer - newObjThisPtr = gtNewIconNode(0, TYP_REF); + if (eeGetEEInfo()->targetAbi == CORINFO_CORERT_ABI) + { + // The dummy argument does not exist in CoreRT + newObjThisPtr = nullptr; + } + else + { + // This is the case for variable-sized objects that are not + // arrays. In this case, call the constructor with a null 'this' + // pointer + newObjThisPtr = gtNewIconNode(0, TYP_REF); + } /* Remember that this basic block contains 'new' of an object */ block->bbFlags |= BBF_HAS_NEWOBJ; diff --git a/src/mscorlib/ref/mscorlib.cs b/src/mscorlib/ref/mscorlib.cs index 965e727ee9..f44978ea25 100644 --- a/src/mscorlib/ref/mscorlib.cs +++ b/src/mscorlib/ref/mscorlib.cs @@ -171,13 +171,14 @@ namespace System public ArithmeticException(string message, System.Exception innerException) { } } [System.Runtime.InteropServices.ComVisibleAttribute(true)] - public abstract partial class Array : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable + public abstract partial class Array : System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.IStructuralComparable, System.Collections.IStructuralEquatable, System.ICloneable { internal Array() { } public bool IsFixedSize { get { return default(bool); } } public bool IsReadOnly { get { return default(bool); } } public bool IsSynchronized { get { return default(bool); } } public int Length { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]get { return default(int); } } + public long LongLength { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]get { return default(long); } } public int Rank { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)]get { return default(int); } } public object SyncRoot { get { return default(object); } } int System.Collections.ICollection.Count { get { return default(int); } } @@ -207,20 +208,28 @@ namespace System [System.Security.SecuritySafeCriticalAttribute] public static void ConstrainedCopy(System.Array sourceArray, int sourceIndex, System.Array destinationArray, int destinationIndex, int length) { } - + public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, System.Converter<TInput, TOutput> converter) { return default(TOutput[]); } + [System.Security.SecuritySafeCriticalAttribute] public static void Copy(System.Array sourceArray, System.Array destinationArray, int length) { } - + public static void Copy(System.Array sourceArray, System.Array destinationArray, long length) { } [System.Security.SecuritySafeCriticalAttribute] public static void Copy(System.Array sourceArray, int sourceIndex, System.Array destinationArray, int destinationIndex, int length) { } + public static void Copy(System.Array sourceArray, long sourceIndex, System.Array destinationArray, long destinationIndex, long length) { } public void CopyTo(System.Array array, int index) { } + public void CopyTo(System.Array array, long index) { } [System.Security.SecuritySafeCriticalAttribute] public static System.Array CreateInstance(System.Type elementType, int length) { return default(System.Array); } [System.Security.SecuritySafeCriticalAttribute] + public static System.Array CreateInstance(System.Type elementType, int length1, int length2) { return default(System.Array); } + [System.Security.SecuritySafeCriticalAttribute] + public static System.Array CreateInstance(System.Type elementType, int length1, int length2, int length3) { return default(System.Array); } + [System.Security.SecuritySafeCriticalAttribute] public static System.Array CreateInstance(System.Type elementType, params int[] lengths) { return default(System.Array); } [System.Security.SecuritySafeCriticalAttribute] public static System.Array CreateInstance(System.Type elementType, int[] lengths, int[] lowerBounds) { return default(System.Array); } - + public static System.Array CreateInstance(System.Type elementType, params long[] lengths) { return default(System.Array); } + public static T[] Empty<T>() { return default(T[]); } public static bool Exists<T>(T[] array, System.Predicate<T> match) { return default(bool); } public static T Find<T>(T[] array, System.Predicate<T> match) { return default(T); } @@ -236,6 +245,7 @@ namespace System public System.Collections.IEnumerator GetEnumerator() { return default(System.Collections.IEnumerator); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)][System.Security.SecuritySafeCriticalAttribute] public int GetLength(int dimension) { return default(int); } + public long GetLongLength(int dimension) { return default(long); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall)] [System.Security.SecuritySafeCriticalAttribute] public int GetLowerBound(int dimension) { return default(int); } @@ -245,8 +255,16 @@ namespace System [System.Security.SecuritySafeCriticalAttribute] public object GetValue(int index) { return default(object); } [System.Security.SecuritySafeCriticalAttribute] + public object GetValue(int index1, int index2) { return default(object); } + [System.Security.SecuritySafeCriticalAttribute] + public object GetValue(int index1, int index2, int index3) { return default(object); } + [System.Security.SecuritySafeCriticalAttribute] public object GetValue(params int[] indices) { return default(object); } - + public object GetValue(long index) { return default(object); } + public object GetValue(long index1, long index2) { return default(object); } + public object GetValue(long index1, long index2, long index3) { return default(object); } + public object GetValue(params long[] indices) { return default(object); } + public static int IndexOf(System.Array array, object value) { return default(int); } public static int IndexOf(System.Array array, object value, int startIndex) { return default(int); } @@ -278,8 +296,16 @@ namespace System [System.Security.SecuritySafeCriticalAttribute] public void SetValue(object value, int index) { } [System.Security.SecuritySafeCriticalAttribute] + public void SetValue(object value, int index1, int index2) { } + [System.Security.SecuritySafeCriticalAttribute] + public void SetValue(object value, int index1, int index2, int index3) { } + [System.Security.SecuritySafeCriticalAttribute] public void SetValue(object value, params int[] indices) { } - + public void SetValue(object value, long index) { } + public void SetValue(object value, long index1, long index2) { } + public void SetValue(object value, long index1, long index2, long index3) { } + public void SetValue(object value, params long[] indices) { } + public static void Sort(System.Array array) { } public static void Sort(System.Array keys, System.Array items) { } @@ -4227,6 +4253,7 @@ namespace System.Collections.Generic public int BinarySearch(int index, int count, T item, System.Collections.Generic.IComparer<T> comparer) { return default(int); } public void Clear() { } public bool Contains(T item) { return default(bool); } + public List<TOutput> ConvertAll<TOutput>(System.Converter<T,TOutput> converter) { throw null; } public void CopyTo(T[] array) { } public void CopyTo(T[] array, int arrayIndex) { } public void CopyTo(int index, T[] array, int arrayIndex, int count) { } diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs index cad47eeb5b..b1f946464e 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventCounter.cs @@ -1,4 +1,8 @@ -using System; +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; using System.Collections; using System.Collections.Generic; using System.Threading; @@ -26,12 +30,12 @@ namespace System.Diagnostics.Tracing { if (name == null) { - throw new ArgumentNullException("name"); + throw new ArgumentNullException(nameof(name)); } if (eventSource == null) { - throw new ArgumentNullException("eventSource"); + throw new ArgumentNullException(nameof(eventSource)); } InitializeBuffer(); @@ -281,7 +285,7 @@ namespace System.Diagnostics.Tracing else if (eventSourceIndex >= EventCounterGroup.s_eventCounterGroups.Length) { EventCounterGroup[] newEventCounterGroups = new EventCounterGroup[eventSourceIndex + 1]; - Array.Copy(EventCounterGroup.s_eventCounterGroups, newEventCounterGroups, EventCounterGroup.s_eventCounterGroups.Length); + Array.Copy(EventCounterGroup.s_eventCounterGroups, 0, newEventCounterGroups, 0, EventCounterGroup.s_eventCounterGroups.Length); EventCounterGroup.s_eventCounterGroups = newEventCounterGroups; } } diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs index 4361bdefd7..11b6e6bac2 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventDescriptor.cs @@ -73,12 +73,12 @@ namespace System.Diagnostics.Tracing { if (id < 0) { - throw new ArgumentOutOfRangeException("id", Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (id > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("id", Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(id), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); } m_traceloggingId = 0; @@ -91,12 +91,12 @@ namespace System.Diagnostics.Tracing if (task < 0) { - throw new ArgumentOutOfRangeException("task", Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); + throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedNonNegNum")); } if (task > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("task", Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); + throw new ArgumentOutOfRangeException(nameof(task), Resources.GetResourceString("ArgumentOutOfRange_NeedValidId", 1, ushort.MaxValue)); } m_task = (ushort)task; diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs index 2b0807f4ee..6ea8d98d92 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventProvider.cs @@ -89,7 +89,7 @@ namespace System.Diagnostics.Tracing private static WriteEventErrorCode s_returnCode; // The last return code private const int s_basicTypeAllocationBufferSize = 16; - private const int s_etwMaxNumberArguments = 64; + private const int s_etwMaxNumberArguments = 128; private const int s_etwAPIMaxRefObjCount = 8; private const int s_maxEventDataDescriptors = 128; private const int s_traceEventMaximumSize = 65482; @@ -560,7 +560,7 @@ namespace System.Diagnostics.Tracing dataStart = 0; if (filterData == null) { -#if !ES_BUILD_PCL && !FEATURE_PAL +#if (!ES_BUILD_PCL && !PROJECTN && !FEATURE_PAL) string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs index 5e025b0966..ce6b9899ec 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource.cs @@ -404,7 +404,7 @@ namespace System.Diagnostics.Tracing public static Guid GetGuid(Type eventSourceType) { if (eventSourceType == null) - throw new ArgumentNullException("eventSourceType"); + throw new ArgumentNullException(nameof(eventSourceType)); Contract.EndContractBlock(); EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute)); @@ -429,7 +429,7 @@ namespace System.Diagnostics.Tracing if (name == null) { - throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), "eventSourceType"); + throw new ArgumentException(Resources.GetResourceString("Argument_InvalidTypeName"), nameof(eventSourceType)); } return GenerateGuidFromName(name.ToUpperInvariant()); // Make it case insensitive. } @@ -472,7 +472,7 @@ namespace System.Diagnostics.Tracing public static string GenerateManifest(Type eventSourceType, string assemblyPathToIncludeInManifest, EventManifestOptions flags) { if (eventSourceType == null) - throw new ArgumentNullException("eventSourceType"); + throw new ArgumentNullException(nameof(eventSourceType)); Contract.EndContractBlock(); byte[] manifestBytes = EventSource.CreateManifestAndDescriptors(eventSourceType, assemblyPathToIncludeInManifest, null, flags); @@ -511,12 +511,12 @@ namespace System.Diagnostics.Tracing public static void SendCommand(EventSource eventSource, EventCommand command, IDictionary<string, string> commandArguments) { if (eventSource == null) - throw new ArgumentNullException("eventSource"); + throw new ArgumentNullException(nameof(eventSource)); // User-defined EventCommands should not conflict with the reserved commands. if ((int)command <= (int)EventCommand.Update && (int)command != (int)EventCommand.SendManifest) { - throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), "command"); + throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidCommand"), nameof(command)); } eventSource.SendCommand(null, 0, 0, command, true, EventLevel.LogAlways, EventKeywords.None, commandArguments); @@ -1451,7 +1451,7 @@ namespace System.Diagnostics.Tracing m_traits = traits; if (m_traits != null && m_traits.Length % 2 != 0) { - throw new ArgumentException(Resources.GetResourceString("TraitEven"), "traits"); + throw new ArgumentException(Resources.GetResourceString("TraitEven"), nameof(traits)); } if (eventSourceGuid == Guid.Empty) @@ -1543,7 +1543,7 @@ namespace System.Diagnostics.Tracing private static string GetName(Type eventSourceType, EventManifestOptions flags) { if (eventSourceType == null) - throw new ArgumentNullException("eventSourceType"); + throw new ArgumentNullException(nameof(eventSourceType)); Contract.EndContractBlock(); EventSourceAttribute attrib = (EventSourceAttribute)GetCustomAttributeHelper(eventSourceType, typeof(EventSourceAttribute), flags); @@ -3672,7 +3672,7 @@ namespace System.Diagnostics.Tracing if (eventData == null || eventData.Length <= eventAttribute.EventId) { EventMetadata[] newValues = new EventMetadata[Math.Max(eventData.Length + 16, eventAttribute.EventId + 1)]; - Array.Copy(eventData, newValues, eventData.Length); + Array.Copy(eventData, 0, newValues, 0, eventData.Length); eventData = newValues; } @@ -3711,7 +3711,7 @@ namespace System.Diagnostics.Tracing if (eventData.Length - idx > 2) // allow one wasted slot. { EventMetadata[] newValues = new EventMetadata[idx + 1]; - Array.Copy(eventData, newValues, newValues.Length); + Array.Copy(eventData, 0, newValues, 0, newValues.Length); eventData = newValues; } } @@ -3994,7 +3994,7 @@ namespace System.Diagnostics.Tracing EventSourceSettings.EtwSelfDescribingEventFormat; if ((settings & evtFormatMask) == evtFormatMask) { - throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), "settings"); + throw new ArgumentException(Resources.GetResourceString("EventSource_InvalidEventFormat"), nameof(settings)); } // If you did not explicitly ask for manifest, you get self-describing. @@ -4351,7 +4351,7 @@ namespace System.Diagnostics.Tracing { if (eventSource == null) { - throw new ArgumentNullException("eventSource"); + throw new ArgumentNullException(nameof(eventSource)); } Contract.EndContractBlock(); @@ -4366,7 +4366,7 @@ namespace System.Diagnostics.Tracing { if (eventSource == null) { - throw new ArgumentNullException("eventSource"); + throw new ArgumentNullException(nameof(eventSource)); } Contract.EndContractBlock(); @@ -6000,7 +6000,7 @@ namespace System.Diagnostics.Tracing internal bool m_activityFilteringEnabled; // does THIS EventSource have activity filtering turned on for this listener? #endif // FEATURE_ACTIVITYSAMPLING - // Only guarenteed to exist after a InsureInit() + // Only guaranteed to exist after a InsureInit() internal EventDispatcher m_Next; // These form a linked list in code:EventSource.m_Dispatchers // Of all listeners for that eventSource. } diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs index b8512bf906..00bd0b7caa 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/EventSource_CoreCLR.cs @@ -178,12 +178,12 @@ namespace System.Diagnostics.Tracing else if ((type.IsArray || type.IsPointer) && type.GetElementType() == typeof(byte)) return "win:Binary"; - ManifestError(Environment.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true); + ManifestError(Resources.GetResourceString("EventSource_UnsupportedEventTypeInManifest", type.Name), true); return string.Empty; } } } - + internal partial class EventProvider { [System.Security.SecurityCritical] diff --git a/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs b/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs index b213cb9dfb..e090c4f106 100644 --- a/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs +++ b/src/mscorlib/src/System/Diagnostics/Eventing/StubEnvironment.cs @@ -226,7 +226,7 @@ namespace Microsoft.Reflection public static bool IsGenericType(this Type type) { return type.IsConstructedGenericType; } public static Type BaseType(this Type type) { return type.GetTypeInfo().BaseType; } public static Assembly Assembly(this Type type) { return type.GetTypeInfo().Assembly; } - public static IEnumerable<PropertyInfo> GetProperties(this Type type) { return type.GetTypeInfo().DeclaredProperties; } + public static IEnumerable<PropertyInfo> GetProperties(this Type type) { return type.GetRuntimeProperties(); } public static MethodInfo GetGetMethod(this PropertyInfo propInfo) { return propInfo.GetMethod; } public static Type[] GetGenericArguments(this Type type) { return type.GenericTypeArguments; } diff --git a/src/mscorlib/src/System/Exception.cs b/src/mscorlib/src/System/Exception.cs index f35f7c5690..434b753165 100644 --- a/src/mscorlib/src/System/Exception.cs +++ b/src/mscorlib/src/System/Exception.cs @@ -891,7 +891,7 @@ namespace System { private SafeSerializationManager _safeSerializationManager; #endif // FEATURE_SERIALIZATION - // See clr\src\vm\excep.h's EXCEPTION_COMPLUS definition: + // See src\inc\corexcep.h's EXCEPTION_COMPLUS definition: private const int _COMPlusExceptionCode = unchecked((int)0xe0434352); // Win32 exception code for COM+ exceptions // InternalToString is called by the runtime to get the exception text diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index fe29112b07..e086717510 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -2570,6 +2570,8 @@ typedef struct _CONTEXT { #define CONTEXT_ALL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS) +#define CONTEXT_XSTATE (CONTEXT_AMD64 | 0x40L) + #define CONTEXT_EXCEPTION_ACTIVE 0x8000000 #define CONTEXT_SERVICE_ACTIVE 0x10000000 #define CONTEXT_EXCEPTION_REQUEST 0x40000000 diff --git a/src/pal/src/arch/i386/asmconstants.h b/src/pal/src/arch/i386/asmconstants.h index 8ec73b4bad..182c1191e4 100644 --- a/src/pal/src/arch/i386/asmconstants.h +++ b/src/pal/src/arch/i386/asmconstants.h @@ -14,6 +14,8 @@ #define CONTEXT_FULL (CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT) +#define CONTEXT_XSTATE 64 + #define CONTEXT_ContextFlags 6*8 #define CONTEXT_SegCs CONTEXT_ContextFlags+8 #define CONTEXT_SegDs CONTEXT_SegCs+2 @@ -47,7 +49,7 @@ #define CONTEXT_Rip CONTEXT_R15+8 #define CONTEXT_FltSave CONTEXT_Rip+8 #define FLOATING_SAVE_AREA_SIZE 4*8+24*16+96 -#define CONTEXT_Xmm0 CONTEXT_FltSave+FLOATING_SAVE_AREA_SIZE // was 10*16 +#define CONTEXT_Xmm0 CONTEXT_FltSave+10*16 #define CONTEXT_Xmm1 CONTEXT_Xmm0+16 #define CONTEXT_Xmm2 CONTEXT_Xmm1+16 #define CONTEXT_Xmm3 CONTEXT_Xmm2+16 @@ -63,7 +65,7 @@ #define CONTEXT_Xmm13 CONTEXT_Xmm12+16 #define CONTEXT_Xmm14 CONTEXT_Xmm13+16 #define CONTEXT_Xmm15 CONTEXT_Xmm14+16 -#define CONTEXT_VectorRegister CONTEXT_Xmm15+16 +#define CONTEXT_VectorRegister CONTEXT_FltSave+FLOATING_SAVE_AREA_SIZE #define CONTEXT_VectorControl CONTEXT_VectorRegister+16*26 #define CONTEXT_DebugControl CONTEXT_VectorControl+8 #define CONTEXT_LastBranchToRip CONTEXT_DebugControl+8 diff --git a/src/pal/src/arch/i386/context2.S b/src/pal/src/arch/i386/context2.S index 6320446a51..0e93e81a55 100644 --- a/src/pal/src/arch/i386/context2.S +++ b/src/pal/src/arch/i386/context2.S @@ -126,6 +126,28 @@ LOCAL_LABEL(Done_Restore_CONTEXT_DEBUG_REGISTERS): fxrstor [rdi + CONTEXT_FltSave] LOCAL_LABEL(Done_Restore_CONTEXT_FLOATING_POINT): + test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_XSTATE + je LOCAL_LABEL(Done_Restore_CONTEXT_XSTATE) + + // Restore the extended state (for now, this is just the upper halves of YMM registers) + vinsertf128 ymm0, ymm0, xmmword ptr [rdi + (CONTEXT_VectorRegister + 0 * 16)], 1 + vinsertf128 ymm1, ymm1, xmmword ptr [rdi + (CONTEXT_VectorRegister + 1 * 16)], 1 + vinsertf128 ymm2, ymm2, xmmword ptr [rdi + (CONTEXT_VectorRegister + 2 * 16)], 1 + vinsertf128 ymm3, ymm3, xmmword ptr [rdi + (CONTEXT_VectorRegister + 3 * 16)], 1 + vinsertf128 ymm4, ymm4, xmmword ptr [rdi + (CONTEXT_VectorRegister + 4 * 16)], 1 + vinsertf128 ymm5, ymm5, xmmword ptr [rdi + (CONTEXT_VectorRegister + 5 * 16)], 1 + vinsertf128 ymm6, ymm6, xmmword ptr [rdi + (CONTEXT_VectorRegister + 6 * 16)], 1 + vinsertf128 ymm7, ymm7, xmmword ptr [rdi + (CONTEXT_VectorRegister + 7 * 16)], 1 + vinsertf128 ymm8, ymm8, xmmword ptr [rdi + (CONTEXT_VectorRegister + 8 * 16)], 1 + vinsertf128 ymm9, ymm9, xmmword ptr [rdi + (CONTEXT_VectorRegister + 9 * 16)], 1 + vinsertf128 ymm10, ymm10, xmmword ptr [rdi + (CONTEXT_VectorRegister + 10 * 16)], 1 + vinsertf128 ymm11, ymm11, xmmword ptr [rdi + (CONTEXT_VectorRegister + 11 * 16)], 1 + vinsertf128 ymm12, ymm12, xmmword ptr [rdi + (CONTEXT_VectorRegister + 12 * 16)], 1 + vinsertf128 ymm13, ymm13, xmmword ptr [rdi + (CONTEXT_VectorRegister + 13 * 16)], 1 + vinsertf128 ymm14, ymm14, xmmword ptr [rdi + (CONTEXT_VectorRegister + 14 * 16)], 1 + vinsertf128 ymm15, ymm15, xmmword ptr [rdi + (CONTEXT_VectorRegister + 15 * 16)], 1 +LOCAL_LABEL(Done_Restore_CONTEXT_XSTATE): + test BYTE PTR [rdi + CONTEXT_ContextFlags], CONTEXT_CONTROL je LOCAL_LABEL(Done_Restore_CONTEXT_CONTROL) diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index 86ea9f98e4..b3ce4b1ff9 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -25,12 +25,14 @@ Revision History: #undef _FILE_OFFSET_BITS #endif +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(DEBUG); // some headers have code with asserts, so do this first + #include "pal/thread.hpp" #include "pal/procobj.hpp" #include "pal/file.hpp" #include "pal/palinternal.h" -#include "pal/dbgmsg.h" #include "pal/process.h" #include "pal/context.h" #include "pal/debug.h" @@ -66,8 +68,6 @@ Revision History: using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(DEBUG); - extern "C" void DBG_DebugBreak_End(); #if HAVE_PROCFS_CTL diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp index a483509f07..af1dc89fb5 100644 --- a/src/pal/src/exception/machexception.cpp +++ b/src/pal/src/exception/machexception.cpp @@ -14,12 +14,14 @@ Abstract: --*/ +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first + #include "pal/thread.hpp" #include "pal/seh.hpp" #include "pal/palinternal.h" #if HAVE_MACH_EXCEPTIONS #include "machexception.h" -#include "pal/dbgmsg.h" #include "pal/critsect.h" #include "pal/debug.h" #include "pal/init.h" @@ -42,8 +44,6 @@ Abstract: using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); - // The port we use to handle exceptions and to set the thread context mach_port_t s_ExceptionPort; diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index 8dd75ac185..c2c217993a 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -18,6 +18,9 @@ Abstract: --*/ +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); // some headers have code with asserts, so do this first + #include "pal/corunix.hpp" #include "pal/handleapi.hpp" #include "pal/thread.hpp" @@ -27,7 +30,6 @@ Abstract: #include "pal/palinternal.h" #if !HAVE_MACH_EXCEPTIONS -#include "pal/dbgmsg.h" #include "pal/init.h" #include "pal/process.h" #include "pal/debug.h" @@ -43,8 +45,6 @@ Abstract: using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(EXCEPT); - #ifdef SIGRTMIN #define INJECT_ACTIVATION_SIGNAL SIGRTMIN #endif @@ -611,10 +611,16 @@ static bool common_signal_handler(int code, siginfo_t *siginfo, void *sigcontext // which is required for restoring context RtlCaptureContext(contextRecord); + ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT; + +#if defined(_AMD64_) + contextFlags |= CONTEXT_XSTATE; +#endif + // Fill context record with required information. from pal.h: // On non-Win32 platforms, the CONTEXT pointer in the // PEXCEPTION_POINTERS will contain at least the CONTEXT_CONTROL registers. - CONTEXTFromNativeContext(ucontext, contextRecord, CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT); + CONTEXTFromNativeContext(ucontext, contextRecord, contextFlags); /* Unmask signal so we can receive it again */ sigemptyset(&signal_set); diff --git a/src/pal/src/include/pal/context.h b/src/pal/src/include/pal/context.h index 6f1b3fe734..5e378942fb 100644 --- a/src/pal/src/include/pal/context.h +++ b/src/pal/src/include/pal/context.h @@ -121,19 +121,71 @@ typedef ucontext_t native_context_t; #define MCREG_R14(mc) ((mc).gregs[REG_R14]) #define MCREG_R15(mc) ((mc).gregs[REG_R15]) -#define FPREG_Xmm(uc, index) *(M128A*)&((uc)->uc_mcontext.fpregs->_xmm[index]) - -#define FPREG_St(uc, index) *(M128A*)&((uc)->uc_mcontext.fpregs->_st[index]) - -#define FPREG_ControlWord(uc) ((uc)->uc_mcontext.fpregs->cwd) -#define FPREG_StatusWord(uc) ((uc)->uc_mcontext.fpregs->swd) -#define FPREG_TagWord(uc) ((uc)->uc_mcontext.fpregs->ftw) -#define FPREG_ErrorOffset(uc) *(DWORD*)&((uc)->uc_mcontext.fpregs->rip) -#define FPREG_ErrorSelector(uc) *(((WORD*)&((uc)->uc_mcontext.fpregs->rip)) + 2) -#define FPREG_DataOffset(uc) *(DWORD*)&((uc)->uc_mcontext.fpregs->rdp) -#define FPREG_DataSelector(uc) *(((WORD*)&((uc)->uc_mcontext.fpregs->rdp)) + 2) -#define FPREG_MxCsr(uc) ((uc)->uc_mcontext.fpregs->mxcsr) -#define FPREG_MxCsr_Mask(uc) ((uc)->uc_mcontext.fpregs->mxcr_mask) +#define FPREG_Fpstate(uc) ((uc)->uc_mcontext.fpregs) +#define FPREG_Xmm(uc, index) *(M128A*)&(FPREG_Fpstate(uc)->_xmm[index]) + +#define FPREG_St(uc, index) *(M128A*)&(FPREG_Fpstate(uc)->_st[index]) + +#define FPREG_ControlWord(uc) (FPREG_Fpstate(uc)->cwd) +#define FPREG_StatusWord(uc) (FPREG_Fpstate(uc)->swd) +#define FPREG_TagWord(uc) (FPREG_Fpstate(uc)->ftw) +#define FPREG_ErrorOffset(uc) *(DWORD*)&(FPREG_Fpstate(uc)->rip) +#define FPREG_ErrorSelector(uc) *(((WORD*)&(FPREG_Fpstate(uc)->rip)) + 2) +#define FPREG_DataOffset(uc) *(DWORD*)&(FPREG_Fpstate(uc)->rdp) +#define FPREG_DataSelector(uc) *(((WORD*)&(FPREG_Fpstate(uc)->rdp)) + 2) +#define FPREG_MxCsr(uc) (FPREG_Fpstate(uc)->mxcsr) +#define FPREG_MxCsr_Mask(uc) (FPREG_Fpstate(uc)->mxcr_mask) + +///////////////////// +// Extended state + +inline _fpx_sw_bytes *FPREG_FpxSwBytes(const ucontext_t *uc) +{ + // Bytes 464..511 in the FXSAVE format are available for software to use for any purpose. In this case, they are used to + // indicate information about extended state. + _ASSERTE(reinterpret_cast<UINT8 *>(&FPREG_Fpstate(uc)->padding[12]) - reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) == 464); + + _ASSERTE(FPREG_Fpstate(uc) != nullptr); + + return reinterpret_cast<_fpx_sw_bytes *>(&FPREG_Fpstate(uc)->padding[12]); +} + +inline UINT32 FPREG_ExtendedSize(const ucontext_t *uc) +{ + _ASSERTE(FPREG_FpxSwBytes(uc)->magic1 == FP_XSTATE_MAGIC1); + return FPREG_FpxSwBytes(uc)->extended_size; +} + +inline bool FPREG_HasExtendedState(const ucontext_t *uc) +{ + // See comments in /usr/include/x86_64-linux-gnu/asm/sigcontext.h for info on how to detect if extended state is present + static_assert_no_msg(FP_XSTATE_MAGIC2_SIZE == sizeof(UINT32)); + + if (FPREG_FpxSwBytes(uc)->magic1 != FP_XSTATE_MAGIC1) + { + return false; + } + + UINT32 extendedSize = FPREG_ExtendedSize(uc); + if (extendedSize < sizeof(_xstate)) + { + return false; + } + + _ASSERTE(extendedSize >= FP_XSTATE_MAGIC2_SIZE); + return *reinterpret_cast<UINT32 *>(reinterpret_cast<UINT8 *>(FPREG_Fpstate(uc)) + (extendedSize - FP_XSTATE_MAGIC2_SIZE)) + == FP_XSTATE_MAGIC2; +} + +inline void *FPREG_Xstate_Ymmh(const ucontext_t *uc) +{ + static_assert_no_msg(sizeof(reinterpret_cast<_xstate *>(FPREG_Fpstate(uc))->ymmh.ymmh_space) == 16 * 16); + _ASSERTE(FPREG_HasExtendedState(uc)); + + return reinterpret_cast<_xstate *>(FPREG_Fpstate(uc))->ymmh.ymmh_space; +} + +///////////////////// #else // BIT64 diff --git a/src/pal/src/include/pal/virtual.h b/src/pal/src/include/pal/virtual.h index 028d83f0f1..a4e225281e 100644 --- a/src/pal/src/include/pal/virtual.h +++ b/src/pal/src/include/pal/virtual.h @@ -28,10 +28,10 @@ extern "C" typedef struct _CMI { struct _CMI * pNext; /* Link to the next entry. */ - struct _CMI * pLast; /* Link to the previous entry. */ + struct _CMI * pPrevious; /* Link to the previous entry. */ - UINT_PTR startBoundary; /* Starting location of the region. */ - SIZE_T memSize; /* Size of the entire region.. */ + UINT_PTR startBoundary; /* Starting location of the region. */ + SIZE_T memSize; /* Size of the entire region.. */ DWORD accessProtection; /* Initial allocation access protection. */ DWORD allocationType; /* Initial allocation type. */ diff --git a/src/pal/src/map/virtual.cpp b/src/pal/src/map/virtual.cpp index c3891494fd..e68ab7849f 100644 --- a/src/pal/src/map/virtual.cpp +++ b/src/pal/src/map/virtual.cpp @@ -8,7 +8,7 @@ Module Name: - virtual.c + virtual.cpp Abstract: @@ -70,9 +70,80 @@ static LPVOID ReserveVirtualMemory( // A memory allocator that allocates memory from a pre-reserved region -// of virtual memory that is located near the coreclr library. +// of virtual memory that is located near the CoreCLR library. static ExecutableMemoryAllocator g_executableMemoryAllocator; +// +// +// Virtual Memory Logging +// +// We maintain a lightweight in-memory circular buffer recording virtual +// memory operations so that we can better diagnose failures and crashes +// caused by one of these operations mishandling memory in some way. +// +// +namespace VirtualMemoryLogging +{ + // Specifies the operation being logged + enum class VirtualOperation + { + Allocate = 0x10, + Reserve = 0x20, + Commit = 0x30, + Decommit = 0x40, + Release = 0x50, + }; + + // Indicates that the attempted operation has failed + const DWORD FailedOperationMarker = 0x80000000; + + // An entry in the in-memory log + struct LogRecord + { + LONG RecordId; + DWORD Operation; + LPVOID CurrentThread; + LPVOID RequestedAddress; + LPVOID ReturnedAddress; + SIZE_T Size; + DWORD AllocationType; + DWORD Protect; + }; + + // Maximum number of records in the in-memory log + const LONG MaxRecords = 128; + + // Buffer used to store the logged data + volatile LogRecord logRecords[MaxRecords]; + + // Current record number. Use (recordNumber % MaxRecords) to determine + // the current position in the circular buffer. + volatile LONG recordNumber = 0; + + // Record an entry in the in-memory log + void LogVaOperation( + IN VirtualOperation operation, + IN LPVOID requestedAddress, + IN SIZE_T size, + IN DWORD flAllocationType, + IN DWORD flProtect, + IN LPVOID returnedAddress, + IN BOOL result) + { + LONG i = InterlockedIncrement(&recordNumber) - 1; + LogRecord* curRec = (LogRecord*)&logRecords[i % MaxRecords]; + + curRec->RecordId = i; + curRec->CurrentThread = (LPVOID)pthread_self(); + curRec->RequestedAddress = requestedAddress; + curRec->ReturnedAddress = returnedAddress; + curRec->Size = size; + curRec->AllocationType = flAllocationType; + curRec->Protect = flProtect; + curRec->Operation = static_cast<DWORD>(operation) | (result ? 0 : FailedOperationMarker); + } +} + /*++ Function: VIRTUALInitialize() @@ -88,7 +159,7 @@ extern "C" BOOL VIRTUALInitialize(bool initializeExecutableMemoryAllocator) { - TRACE( "Initializing the Virtual Critical Sections. \n" ); + TRACE("Initializing the Virtual Critical Sections. \n"); InternalInitializeCriticalSection(&virtual_critsec); @@ -226,7 +297,7 @@ static INT VIRTUALGetAllocationType( SIZE_T Index, CONST PCMI pInformation ) * IN BYTE* pBitArray - A pointer the array to be manipulated. * * Returns TRUE on success, FALSE otherwise. - * Turn on/off memory staus bits. + * Turn on/off memory status bits. * */ static BOOL VIRTUALSetPageBits ( UINT nStatus, SIZE_T nStartingBit, @@ -445,20 +516,20 @@ static BOOL VIRTUALReleaseMemory( PCMI pMemoryToBeReleased ) pVirtualMemory = pMemoryToBeReleased->pNext; if ( pMemoryToBeReleased->pNext ) { - pMemoryToBeReleased->pNext->pLast = NULL; + pMemoryToBeReleased->pNext->pPrevious = NULL; } } else /* Could be anywhere in the list. */ { /* Delete the entry from the linked list. */ - if ( pMemoryToBeReleased->pLast ) + if ( pMemoryToBeReleased->pPrevious ) { - pMemoryToBeReleased->pLast->pNext = pMemoryToBeReleased->pNext; + pMemoryToBeReleased->pPrevious->pNext = pMemoryToBeReleased->pNext; } if ( pMemoryToBeReleased->pNext ) { - pMemoryToBeReleased->pNext->pLast = pMemoryToBeReleased->pLast; + pMemoryToBeReleased->pNext->pPrevious = pMemoryToBeReleased->pPrevious; } } @@ -594,7 +665,7 @@ static void VIRTUALDisplayList( void ) DBGOUT( "\t accessProtection %d \n", p->accessProtection ); DBGOUT( "\t allocationType %d \n", p->allocationType ); DBGOUT( "\t pNext %p \n", p->pNext ); - DBGOUT( "\t pLast %p \n", p->pLast ); + DBGOUT( "\t pLast %p \n", p->pPrevious ); count++; p = p->pNext; @@ -604,6 +675,30 @@ static void VIRTUALDisplayList( void ) } #endif +#ifdef DEBUG +void VerifyRightEntry(PCMI pEntry) +{ + volatile PCMI pRight = pEntry->pNext; + SIZE_T endAddress; + if (pRight != nullptr) + { + endAddress = ((SIZE_T)pEntry->startBoundary) + pEntry->memSize; + _ASSERTE(endAddress <= (SIZE_T)pRight->startBoundary); + } +} + +void VerifyLeftEntry(PCMI pEntry) +{ + volatile PCMI pLeft = pEntry->pPrevious; + SIZE_T endAddress; + if (pLeft != NULL) + { + endAddress = ((SIZE_T)pLeft->startBoundary) + pLeft->memSize; + _ASSERTE(endAddress <= (SIZE_T)pEntry->startBoundary); + } +} +#endif // DEBUG + /**** * VIRTUALStoreAllocationInfo() * @@ -611,107 +706,106 @@ static void VIRTUALDisplayList( void ) * NOTE: The caller must own the critical section. */ static BOOL VIRTUALStoreAllocationInfo( - IN UINT_PTR startBoundary, /* Start of the region. */ - IN SIZE_T memSize, /* Size of the region. */ + IN UINT_PTR startBoundary, /* Start of the region. */ + IN SIZE_T memSize, /* Size of the region. */ IN DWORD flAllocationType, /* Allocation Types. */ IN DWORD flProtection ) /* Protections flags on the memory. */ { - PCMI pNewEntry = NULL; - PCMI pMemInfo = NULL; - BOOL bRetVal = TRUE; + PCMI pNewEntry = nullptr; + PCMI pMemInfo = nullptr; SIZE_T nBufferSize = 0; - if ( ( memSize & VIRTUAL_PAGE_MASK ) != 0 ) + if ((memSize & VIRTUAL_PAGE_MASK) != 0) { - ERROR( "The memory size was not in multiples of the page size. \n" ); - bRetVal = FALSE; - goto done; + ERROR("The memory size was not a multiple of the page size. \n"); + return FALSE; } - - if ( !(pNewEntry = ( PCMI )InternalMalloc( sizeof( *pNewEntry )) ) ) + + if (!(pNewEntry = (PCMI)InternalMalloc(sizeof(*pNewEntry)))) { ERROR( "Unable to allocate memory for the structure.\n"); - bRetVal = FALSE; - goto done; + return FALSE; } - + pNewEntry->startBoundary = startBoundary; pNewEntry->memSize = memSize; pNewEntry->allocationType = flAllocationType; pNewEntry->accessProtection = flProtection; - + nBufferSize = memSize / VIRTUAL_PAGE_SIZE / CHAR_BIT; - if ( ( memSize / VIRTUAL_PAGE_SIZE ) % CHAR_BIT != 0 ) + if ((memSize / VIRTUAL_PAGE_SIZE) % CHAR_BIT != 0) { nBufferSize++; } - - pNewEntry->pAllocState = (BYTE*)InternalMalloc( nBufferSize ); - pNewEntry->pProtectionState = (BYTE*)InternalMalloc( (memSize / VIRTUAL_PAGE_SIZE) ); + + pNewEntry->pAllocState = (BYTE*)InternalMalloc(nBufferSize); + pNewEntry->pProtectionState = (BYTE*)InternalMalloc((memSize / VIRTUAL_PAGE_SIZE)); if (pNewEntry->pAllocState && pNewEntry->pProtectionState) { /* Set the intial allocation state, and initial allocation protection. */ - VIRTUALSetAllocState( MEM_RESERVE, 0, nBufferSize * CHAR_BIT, pNewEntry ); - memset( pNewEntry->pProtectionState, - VIRTUALConvertWinFlags( flProtection ), - memSize / VIRTUAL_PAGE_SIZE ); + VIRTUALSetAllocState(MEM_RESERVE, 0, nBufferSize * CHAR_BIT, pNewEntry); + memset(pNewEntry->pProtectionState, + VIRTUALConvertWinFlags(flProtection), + memSize / VIRTUAL_PAGE_SIZE); } else { ERROR( "Unable to allocate memory for the structure.\n"); - bRetVal = FALSE; - if (pNewEntry->pProtectionState) InternalFree( pNewEntry->pProtectionState ); - pNewEntry->pProtectionState = NULL; + if (pNewEntry->pProtectionState) InternalFree(pNewEntry->pProtectionState); + pNewEntry->pProtectionState = nullptr; - if (pNewEntry->pAllocState) InternalFree( pNewEntry->pAllocState ); - pNewEntry->pAllocState = NULL; + if (pNewEntry->pAllocState) InternalFree(pNewEntry->pAllocState); + pNewEntry->pAllocState = nullptr; - InternalFree( pNewEntry ); - pNewEntry = NULL; - - goto done; + InternalFree(pNewEntry); + pNewEntry = nullptr; + + return FALSE; } pMemInfo = pVirtualMemory; - if ( pMemInfo && pMemInfo->startBoundary < startBoundary ) + if (pMemInfo && pMemInfo->startBoundary < startBoundary) { /* Look for the correct insert point */ - TRACE( "Looking for the correct insert location.\n"); - while ( pMemInfo->pNext && ( pMemInfo->pNext->startBoundary < startBoundary ) ) + TRACE("Looking for the correct insert location.\n"); + while (pMemInfo->pNext && (pMemInfo->pNext->startBoundary < startBoundary)) { pMemInfo = pMemInfo->pNext; } - + pNewEntry->pNext = pMemInfo->pNext; - pNewEntry->pLast = pMemInfo; - - if ( pNewEntry->pNext ) + pNewEntry->pPrevious = pMemInfo; + + if (pNewEntry->pNext) { - pNewEntry->pNext->pLast = pNewEntry; + pNewEntry->pNext->pPrevious = pNewEntry; } - + pMemInfo->pNext = pNewEntry; } else { - TRACE( "Inserting a new element into the linked list\n" ); /* This is the first entry in the list. */ pNewEntry->pNext = pMemInfo; - pNewEntry->pLast = NULL; - - if ( pNewEntry->pNext ) + pNewEntry->pPrevious = nullptr; + + if (pNewEntry->pNext) { - pNewEntry->pNext->pLast = pNewEntry; + pNewEntry->pNext->pPrevious = pNewEntry; } - + pVirtualMemory = pNewEntry ; } -done: - TRACE( "Exiting StoreAllocationInformation. \n" ); - return bRetVal; + +#ifdef DEBUG + VerifyRightEntry(pNewEntry); + VerifyLeftEntry(pNewEntry); +#endif // DEBUG + + return TRUE; } /****** @@ -778,6 +872,15 @@ static LPVOID VIRTUALReserveMemory( } } + LogVaOperation( + VirtualMemoryLogging::VirtualOperation::Reserve, + lpAddress, + dwSize, + flAllocationType, + flProtect, + pRetVal, + pRetVal != NULL); + InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec); return pRetVal; } @@ -1058,6 +1161,16 @@ error: } done: + + LogVaOperation( + VirtualMemoryLogging::VirtualOperation::Commit, + lpAddress, + dwSize, + flAllocationType, + flProtect, + pRetVal, + pRetVal != NULL); + return pRetVal; } @@ -1117,6 +1230,15 @@ VirtualAlloc( WARN( "Ignoring the allocation flag MEM_TOP_DOWN.\n" ); } + LogVaOperation( + VirtualMemoryLogging::VirtualOperation::Allocate, + lpAddress, + dwSize, + flAllocationType, + flProtect, + NULL, + TRUE); + if ( flAllocationType & MEM_RESERVE ) { InternalEnterCriticalSection(pthrCurrent, &virtual_critsec); @@ -1325,6 +1447,17 @@ VirtualFree( } VirtualFreeExit: + + LogVaOperation( + (dwFreeType & MEM_DECOMMIT) ? VirtualMemoryLogging::VirtualOperation::Decommit + : VirtualMemoryLogging::VirtualOperation::Release, + lpAddress, + dwSize, + dwFreeType, + 0, + NULL, + bRetVal); + InternalLeaveCriticalSection(pthrCurrent, &virtual_critsec); LOGEXIT( "VirtualFree returning %s.\n", bRetVal == TRUE ? "TRUE" : "FALSE" ); PERF_EXIT(VirtualFree); diff --git a/src/pal/src/thread/context.cpp b/src/pal/src/thread/context.cpp index 025bb978c6..9aaf105d74 100644 --- a/src/pal/src/thread/context.cpp +++ b/src/pal/src/thread/context.cpp @@ -19,8 +19,10 @@ Abstract: --*/ -#include "pal/palinternal.h" #include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first + +#include "pal/palinternal.h" #include "pal/context.h" #include "pal/debug.h" #include "pal/thread.hpp" @@ -29,8 +31,6 @@ Abstract: #include <errno.h> #include <unistd.h> -SET_DEFAULT_DEBUG_CHANNEL(THREAD); - extern PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode; // in context2.S @@ -465,6 +465,15 @@ void CONTEXTToNativeContext(CONST CONTEXT *lpContext, native_context_t *native) } #endif } + + // TODO: Enable for all Unix systems +#if defined(_AMD64_) && defined(__linux__) + if ((lpContext->ContextFlags & CONTEXT_XSTATE) != 0) + { + _ASSERTE(FPREG_HasExtendedState(native)); + memcpy_s(FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16, lpContext->VectorRegister, sizeof(M128A) * 16); + } +#endif // _AMD64_ } /*++ @@ -513,16 +522,19 @@ void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContex if (native->uc_mcontext.__fpregs == nullptr) #endif { - // Reset the CONTEXT_FLOATING_POINT bit(s) so it's clear that the floating point - // data in the CONTEXT is not valid. Since CONTEXT_FLOATING_POINT is defined as - // the architecture bit(s) OR'd with one or more other bits, we first get the bits - // that are unique to CONTEXT_FLOATING_POINT by resetting the architecture bits. - // We determine what those are by inverting the union of CONTEXT_CONTROL and - // CONTEXT_INTEGER, both of which should also have the architecture bit(s) set. + // Reset the CONTEXT_FLOATING_POINT bit(s) and the CONTEXT_XSTATE bit(s) so it's + // clear that the floating point and extended state data in the CONTEXT is not + // valid. Since these flags are defined as the architecture bit(s) OR'd with one + // or more other bits, we first get the bits that are unique to each by resetting + // the architecture bits. We determine what those are by inverting the union of + // CONTEXT_CONTROL and CONTEXT_INTEGER, both of which should also have the + // architecture bit(s) set. const ULONG floatingPointFlags = CONTEXT_FLOATING_POINT & ~(CONTEXT_CONTROL & CONTEXT_INTEGER); - lpContext->ContextFlags &= ~floatingPointFlags; + const ULONG xstateFlags = CONTEXT_XSTATE & ~(CONTEXT_CONTROL & CONTEXT_INTEGER); + + lpContext->ContextFlags &= ~(floatingPointFlags | xstateFlags); - // Bail out regardless of whether the caller wanted CONTEXT_FLOATING_POINT + // Bail out regardless of whether the caller wanted CONTEXT_FLOATING_POINT or CONTEXT_XSTATE return; } #endif @@ -551,6 +563,24 @@ void CONTEXTFromNativeContext(const native_context_t *native, LPCONTEXT lpContex } #endif } + + // TODO: Enable for all Unix systems +#if defined(_AMD64_) && defined(__linux__) + if ((contextFlags & CONTEXT_XSTATE) != 0) + { + if (FPREG_HasExtendedState(native)) + { + memcpy_s(lpContext->VectorRegister, sizeof(M128A) * 16, FPREG_Xstate_Ymmh(native), sizeof(M128A) * 16); + } + else + { + // Reset the CONTEXT_XSTATE bit(s) so it's clear that the extended state data in + // the CONTEXT is not valid. + const ULONG xstateFlags = CONTEXT_XSTATE & ~(CONTEXT_CONTROL & CONTEXT_INTEGER); + lpContext->ContextFlags &= ~xstateFlags; + } + } +#endif // _AMD64_ } /*++ diff --git a/src/pal/src/thread/thread.cpp b/src/pal/src/thread/thread.cpp index 159c451224..d6f6f9c47a 100644 --- a/src/pal/src/thread/thread.cpp +++ b/src/pal/src/thread/thread.cpp @@ -18,6 +18,9 @@ Abstract: --*/ +#include "pal/dbgmsg.h" +SET_DEFAULT_DEBUG_CHANNEL(THREAD); // some headers have code with asserts, so do this first + #include "pal/corunix.hpp" #include "pal/context.h" #include "pal/thread.hpp" @@ -29,7 +32,6 @@ Abstract: #include "procprivate.hpp" #include "pal/process.h" #include "pal/module.h" -#include "pal/dbgmsg.h" #include "pal/environ.h" #include "pal/init.h" @@ -74,7 +76,6 @@ using namespace CorUnix; /* ------------------- Definitions ------------------------------*/ -SET_DEFAULT_DEBUG_CHANNEL(THREAD); // The default stack size of a newly created thread (currently 256KB) // when the dwStackSize parameter of PAL_CreateThread() diff --git a/src/vm/CMakeLists.txt b/src/vm/CMakeLists.txt index 89a6437da1..66fc6432af 100644 --- a/src/vm/CMakeLists.txt +++ b/src/vm/CMakeLists.txt @@ -27,6 +27,13 @@ if(CLR_CMAKE_PLATFORM_UNIX) add_compile_options(-fPIC) endif(CLR_CMAKE_PLATFORM_UNIX) +if(FEATURE_GDBJIT) + set(VM_SOURCES_GDBJIT + gdbjit.cpp + ) + add_definitions(-DFEATURE_GDBJIT) +endif(FEATURE_GDBJIT) + set(VM_SOURCES_DAC_AND_WKS_COMMON appdomain.cpp array.cpp @@ -109,6 +116,7 @@ set(VM_SOURCES_DAC_AND_WKS_COMMON virtualcallstub.cpp win32threadpool.cpp zapsig.cpp + ${VM_SOURCES_GDBJIT} ) if(FEATURE_READYTORUN) diff --git a/src/vm/gcenv.os.cpp b/src/vm/gcenv.os.cpp index 1972b23ea7..73b21a7a0b 100644 --- a/src/vm/gcenv.os.cpp +++ b/src/vm/gcenv.os.cpp @@ -534,9 +534,14 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit() return memStatus.ullTotalPhys; } -// Get global memory status +// Get memory status // Parameters: -// ms - pointer to the structure that will be filled in with the memory status +// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory +// that is in use (0 indicates no memory use and 100 indicates full memory use). +// available_physical - The amount of physical memory currently available, in bytes. +// available_page_file - The maximum amount of memory the current process can commit, in bytes. +// Remarks: +// Any parameter can be null. void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { LIMITED_METHOD_CONTRACT; diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp new file mode 100644 index 0000000000..435dec4da3 --- /dev/null +++ b/src/vm/gdbjit.cpp @@ -0,0 +1,1003 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// File: gdbjit.cpp +// + +// +// NotifyGdb implementation. +// +//***************************************************************************** + +#include "common.h" +#include "gdbjit.h" +#include "gdbjithelpers.h" + +struct DebuggerILToNativeMap +{ + ULONG ilOffset; + ULONG nativeStartOffset; + ULONG nativeEndOffset; + ICorDebugInfo::SourceTypes source; +}; +BYTE* DebugInfoStoreNew(void * pData, size_t cBytes) +{ + return new (nothrow) BYTE[cBytes]; +} + +/* Get IL to native offsets map */ +HRESULT +GetMethodNativeMap(MethodDesc* methodDesc, + ULONG32* numMap, + DebuggerILToNativeMap** map) +{ + // Use the DebugInfoStore to get IL->Native maps. + // It doesn't matter whether we're jitted, ngenned etc. + + DebugInfoRequest request; + TADDR nativeCodeStartAddr = PCODEToPINSTR(methodDesc->GetNativeCode()); + request.InitFromStartingAddr(methodDesc, nativeCodeStartAddr); + + // Bounds info. + ULONG32 countMapCopy; + NewHolder<ICorDebugInfo::OffsetMapping> mapCopy(NULL); + + BOOL success = DebugInfoManager::GetBoundariesAndVars(request, + DebugInfoStoreNew, + NULL, // allocator + &countMapCopy, + &mapCopy, + NULL, + NULL); + + if (!success) + { + return E_FAIL; + } + + // Need to convert map formats. + *numMap = countMapCopy; + + *map = new (nothrow) DebuggerILToNativeMap[countMapCopy]; + if (!*map) + { + return E_OUTOFMEMORY; + } + + ULONG32 i; + for (i = 0; i < *numMap; i++) + { + (*map)[i].ilOffset = mapCopy[i].ilOffset; + (*map)[i].nativeStartOffset = mapCopy[i].nativeOffset; + if (i > 0) + { + (*map)[i - 1].nativeEndOffset = (*map)[i].nativeStartOffset; + } + (*map)[i].source = mapCopy[i].source; + } + if (*numMap >= 1) + { + (*map)[i - 1].nativeEndOffset = 0; + } + return S_OK; +} + +/* Get mapping of IL offsets to source line numbers */ +HRESULT +GetDebugInfoFromPDB(MethodDesc* MethodDescPtr, SymbolsInfo** symInfo, unsigned int &symInfoLen) +{ + DebuggerILToNativeMap* map = NULL; + + ULONG32 numMap; + + if (!getInfoForMethodDelegate) + return E_FAIL; + + if (GetMethodNativeMap(MethodDescPtr, &numMap, &map) != S_OK) + return E_FAIL; + + const Module* mod = MethodDescPtr->GetMethodTable()->GetModule(); + SString modName = mod->GetFile()->GetPath(); + StackScratchBuffer scratch; + const char* szModName = modName.GetUTF8(scratch); + + MethodDebugInfo* methodDebugInfo = new (nothrow) MethodDebugInfo(); + if (methodDebugInfo == nullptr) + return E_OUTOFMEMORY; + methodDebugInfo->points = (SequencePointInfo*) CoTaskMemAlloc(sizeof(SequencePointInfo) * numMap); + if (methodDebugInfo->points == nullptr) + return E_OUTOFMEMORY; + methodDebugInfo->size = numMap; + + if (!getInfoForMethodDelegate(szModName, MethodDescPtr->GetMemberDef(), *methodDebugInfo)) + return E_FAIL; + + symInfoLen = methodDebugInfo->size; + *symInfo = new (nothrow) SymbolsInfo[symInfoLen]; + if (*symInfo == nullptr) + return E_FAIL; + + for (ULONG32 i = 0; i < symInfoLen; i++) + { + for (ULONG32 j = 0; j < numMap; j++) + { + if (methodDebugInfo->points[i].ilOffset == map[j].ilOffset) + { + SymbolsInfo& s = (*symInfo)[i]; + const SequencePointInfo& sp = methodDebugInfo->points[i]; + + s.nativeOffset = map[j].nativeStartOffset; + s.ilOffset = map[j].ilOffset; + s.fileIndex = 0; + //wcscpy(s.fileName, sp.fileName); + int len = WideCharToMultiByte(CP_UTF8, 0, sp.fileName, -1, s.fileName, sizeof(s.fileName), NULL, NULL); + s.fileName[len] = 0; + s.lineNumber = sp.lineNumber; + } + } + } + + CoTaskMemFree(methodDebugInfo->points); + return S_OK; +} + +// GDB JIT interface +typedef enum +{ + JIT_NOACTION = 0, + JIT_REGISTER_FN, + JIT_UNREGISTER_FN +} jit_actions_t; + +struct jit_code_entry +{ + struct jit_code_entry *next_entry; + struct jit_code_entry *prev_entry; + const char *symfile_addr; + UINT64 symfile_size; +}; + +struct jit_descriptor +{ + UINT32 version; + /* This type should be jit_actions_t, but we use uint32_t + to be explicit about the bitwidth. */ + UINT32 action_flag; + struct jit_code_entry *relevant_entry; + struct jit_code_entry *first_entry; +}; +// GDB puts a breakpoint in this function. +// To prevent from inlining we add noinline attribute and inline assembler statement. +extern "C" +void __attribute__((noinline)) __jit_debug_register_code() { __asm__(""); }; + +/* Make sure to specify the version statically, because the + debugger may check the version before we can set it. */ +struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 }; + +// END of GDB JIT interface + +/* Predefined section names */ +const char* SectionNames[] = { + "", ".text", ".shstrtab", ".debug_str", ".debug_abbrev", ".debug_info", + ".debug_pubnames", ".debug_pubtypes", ".debug_line", "" +}; + +const int SectionNamesCount = sizeof(SectionNames) / sizeof(SectionNames[0]); + +/* Static data for section headers */ +struct SectionHeader { + uint32_t m_type; + uint64_t m_flags; +} Sections[] = { + {SHT_NULL, 0}, + {SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR}, + {SHT_STRTAB, 0}, + {SHT_PROGBITS, SHF_MERGE | SHF_STRINGS }, + {SHT_PROGBITS, 0}, + {SHT_PROGBITS, 0}, + {SHT_PROGBITS, 0}, + {SHT_PROGBITS, 0}, + {SHT_PROGBITS, 0} +}; + +/* Static data for .debug_str section */ +const char* DebugStrings[] = { + "CoreCLR", "" /* module name */, "" /* module path */, "" /* method name */, "int" +}; + +const int DebugStringCount = sizeof(DebugStrings) / sizeof(DebugStrings[0]); + +/* Static data for .debug_abbrev */ +const unsigned char AbbrevTable[] = { + 1, DW_TAG_compile_unit, DW_CHILDREN_yes, + DW_AT_producer, DW_FORM_strp, DW_AT_language, DW_FORM_data2, DW_AT_name, DW_FORM_strp, + DW_AT_stmt_list, DW_FORM_sec_offset, 0, 0, + 2, DW_TAG_subprogram, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, DW_AT_decl_file, DW_FORM_data1, DW_AT_decl_line, DW_FORM_data1, + DW_AT_type, DW_FORM_ref4, DW_AT_external, DW_FORM_flag_present, 0, 0, + 3, DW_TAG_base_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_strp, DW_AT_encoding, DW_FORM_data1, DW_AT_byte_size, DW_FORM_data1,0, 0, + 0 +}; + +const int AbbrevTableSize = sizeof(AbbrevTable); + +/* Static data for .debug_line, including header */ +#define DWARF_LINE_BASE (-5) +#define DWARF_LINE_RANGE 14 +#define DWARF_OPCODE_BASE 13 + +DwarfLineNumHeader LineNumHeader = { + 0, 2, 0, 1, 1, DWARF_LINE_BASE, DWARF_LINE_RANGE, DWARF_OPCODE_BASE, {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1} +}; + +/* Static data for .debug_info */ +struct __attribute__((packed)) DebugInfo +{ + uint8_t m_cu_abbrev; + uint32_t m_prod_off; + uint16_t m_lang; + uint32_t m_cu_name; + uint32_t m_line_num; + + uint8_t m_sub_abbrev; + uint32_t m_sub_name; + uint8_t m_file, m_line; + uint32_t m_sub_type; + + uint8_t m_type_abbrev; + uint32_t m_type_name; + uint8_t m_encoding; + uint8_t m_byte_size; +} debugInfo = { + 1, 0, DW_LANG_C89, 0, 0, + 2, 0, 1, 1, 37, + 3, 0, DW_ATE_signed, 4 +}; + +/* Create ELF/DWARF debug info for jitted method */ +void NotifyGdb::MethodCompiled(MethodDesc* MethodDescPtr) +{ + PCODE pCode = MethodDescPtr->GetNativeCode(); + + if (pCode == NULL) + return; + unsigned int symInfoLen = 0; + NewArrayHolder<SymbolsInfo> symInfo = nullptr; + + /* Get method name & size of jitted code */ + LPCUTF8 methodName = MethodDescPtr->GetName(); + EECodeInfo codeInfo(pCode); + TADDR codeSize = codeInfo.GetCodeManager()->GetFunctionSize(codeInfo.GetGCInfoToken()); + +#ifdef _TARGET_ARM_ + pCode &= ~1; // clear thumb flag for debug info +#endif + + /* Get module name */ + const Module* mod = MethodDescPtr->GetMethodTable()->GetModule(); + SString modName = mod->GetFile()->GetPath(); + StackScratchBuffer scratch; + const char* szModName = modName.GetUTF8(scratch); + const char *szModulePath, *szModuleFile; + + SplitPathname(szModName, szModulePath, szModuleFile); + + /* Get debug info for method from portable PDB */ + HRESULT hr = GetDebugInfoFromPDB(MethodDescPtr, &symInfo, symInfoLen); + if (FAILED(hr) || symInfoLen == 0) + { + return; + } + + MemBuf elfHeader, sectHeaders, sectStr, dbgInfo, dbgAbbrev, dbgPubname, dbgPubType, dbgLine, dbgStr, elfFile; + + /* Build .debug_abbrev section */ + if (!BuildDebugAbbrev(dbgAbbrev)) + { + return; + } + + /* Build .debug_line section */ + if (!BuildLineTable(dbgLine, pCode, symInfo, symInfoLen)) + { + return; + } + + DebugStrings[1] = szModuleFile; + DebugStrings[3] = methodName; + + /* Build .debug_str section */ + if (!BuildDebugStrings(dbgStr)) + { + return; + } + + /* Build .debug_info section */ + if (!BuildDebugInfo(dbgInfo)) + { + return; + } + + /* Build .debug_pubname section */ + if (!BuildDebugPub(dbgPubname, methodName, dbgInfo.MemSize, 26)) + { + return; + } + + /* Build debug_pubtype section */ + if (!BuildDebugPub(dbgPubType, "int", dbgInfo.MemSize, 37)) + { + return; + } + + /* Build section names section */ + if (!BuildSectionNameTable(sectStr)) + { + return; + } + + /* Build section headers table */ + if (!BuildSectionTable(sectHeaders)) + { + return; + } + + /* Patch section offsets & sizes */ + long offset = sizeof(Elf_Ehdr); + Elf_Shdr* pShdr = reinterpret_cast<Elf_Shdr*>(sectHeaders.MemPtr.GetValue()); + ++pShdr; // .text + pShdr->sh_addr = pCode; + pShdr->sh_size = codeSize; + ++pShdr; // .shstrtab + pShdr->sh_offset = offset; + pShdr->sh_size = sectStr.MemSize; + offset += sectStr.MemSize; + ++pShdr; // .debug_str + pShdr->sh_offset = offset; + pShdr->sh_size = dbgStr.MemSize; + offset += dbgStr.MemSize; + ++pShdr; // .debug_abbrev + pShdr->sh_offset = offset; + pShdr->sh_size = dbgAbbrev.MemSize; + offset += dbgAbbrev.MemSize; + ++pShdr; // .debug_info + pShdr->sh_offset = offset; + pShdr->sh_size = dbgInfo.MemSize; + offset += dbgInfo.MemSize; + ++pShdr; // .debug_pubnames + pShdr->sh_offset = offset; + pShdr->sh_size = dbgPubname.MemSize; + offset += dbgPubname.MemSize; + ++pShdr; // .debug_pubtypes + pShdr->sh_offset = offset; + pShdr->sh_size = dbgPubType.MemSize; + offset += dbgPubType.MemSize; + ++pShdr; // .debug_line + pShdr->sh_offset = offset; + pShdr->sh_size = dbgLine.MemSize; + offset += dbgLine.MemSize; + + /* Build ELF header */ + if (!BuildELFHeader(elfHeader)) + { + return; + } + Elf_Ehdr* header = reinterpret_cast<Elf_Ehdr*>(elfHeader.MemPtr.GetValue()); +#ifdef _TARGET_ARM_ + header->e_flags = EF_ARM_EABI_VER5; +#ifdef ARM_SOFTFP + header->e_flags |= EF_ARM_SOFT_FLOAT; +#else + header->e_flags |= EF_ARM_VFP_FLOAT; +#endif +#endif + header->e_shoff = offset; + header->e_shentsize = sizeof(Elf_Shdr); + header->e_shnum = SectionNamesCount - 1; + header->e_shstrndx = 2; + + /* Build ELF image in memory */ + elfFile.MemSize = elfHeader.MemSize + sectStr.MemSize + dbgStr.MemSize + dbgAbbrev.MemSize + + dbgInfo.MemSize + dbgPubname.MemSize + dbgPubType.MemSize + dbgLine.MemSize + sectHeaders.MemSize; + elfFile.MemPtr = new (nothrow) char[elfFile.MemSize]; + if (elfFile.MemPtr == nullptr) + { + return; + } + + /* Copy section data */ + offset = 0; + memcpy(elfFile.MemPtr, elfHeader.MemPtr, elfHeader.MemSize); + offset += elfHeader.MemSize; + memcpy(elfFile.MemPtr + offset, sectStr.MemPtr, sectStr.MemSize); + offset += sectStr.MemSize; + memcpy(elfFile.MemPtr + offset, dbgStr.MemPtr, dbgStr.MemSize); + offset += dbgStr.MemSize; + memcpy(elfFile.MemPtr + offset, dbgAbbrev.MemPtr, dbgAbbrev.MemSize); + offset += dbgAbbrev.MemSize; + memcpy(elfFile.MemPtr + offset, dbgInfo.MemPtr, dbgInfo.MemSize); + offset += dbgInfo.MemSize; + memcpy(elfFile.MemPtr + offset, dbgPubname.MemPtr, dbgPubname.MemSize); + offset += dbgPubname.MemSize; + memcpy(elfFile.MemPtr + offset, dbgPubType.MemPtr, dbgPubType.MemSize); + offset += dbgPubType.MemSize; + memcpy(elfFile.MemPtr + offset, dbgLine.MemPtr, dbgLine.MemSize); + offset += dbgLine.MemSize; + memcpy(elfFile.MemPtr + offset, sectHeaders.MemPtr, sectHeaders.MemSize); + + /* Create GDB JIT structures */ + jit_code_entry* jit_symbols = new (nothrow) jit_code_entry; + + if (jit_symbols == nullptr) + { + return; + } + + /* Fill the new entry */ + jit_symbols->next_entry = jit_symbols->prev_entry = 0; + jit_symbols->symfile_addr = elfFile.MemPtr; + jit_symbols->symfile_size = elfFile.MemSize; + + /* Link into list */ + jit_code_entry *head = __jit_debug_descriptor.first_entry; + __jit_debug_descriptor.first_entry = jit_symbols; + if (head != 0) + { + jit_symbols->next_entry = head; + head->prev_entry = jit_symbols; + } + + /* Notify the debugger */ + __jit_debug_descriptor.relevant_entry = jit_symbols; + __jit_debug_descriptor.action_flag = JIT_REGISTER_FN; + __jit_debug_register_code(); + +} + +void NotifyGdb::MethodDropped(MethodDesc* MethodDescPtr) +{ + PCODE pCode = MethodDescPtr->GetNativeCode(); + + if (pCode == NULL) + return; + + /* Find relevant entry */ + for (jit_code_entry* jit_symbols = __jit_debug_descriptor.first_entry; jit_symbols != 0; jit_symbols = jit_symbols->next_entry) + { + const char* ptr = jit_symbols->symfile_addr; + uint64_t size = jit_symbols->symfile_size; + + const Elf_Ehdr* pEhdr = reinterpret_cast<const Elf_Ehdr*>(ptr); + const Elf_Shdr* pShdr = reinterpret_cast<const Elf_Shdr*>(ptr + pEhdr->e_shoff); + ++pShdr; // bump to .text section + if (pShdr->sh_addr == pCode) + { + /* Notify the debugger */ + __jit_debug_descriptor.relevant_entry = jit_symbols; + __jit_debug_descriptor.action_flag = JIT_UNREGISTER_FN; + __jit_debug_register_code(); + + /* Free memory */ + delete[] ptr; + + /* Unlink from list */ + if (jit_symbols->prev_entry == 0) + __jit_debug_descriptor.first_entry = jit_symbols->next_entry; + else + jit_symbols->prev_entry->next_entry = jit_symbols->next_entry; + delete jit_symbols; + break; + } + } +} + +/* Build the DWARF .debug_line section */ +bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines) +{ + MemBuf fileTable, lineProg; + + /* Build file table */ + if (!BuildFileTable(fileTable, lines, nlines)) + return false; + /* Build line info program */ + if (!BuildLineProg(lineProg, startAddr, lines, nlines)) + { + return false; + } + + buf.MemSize = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize + lineProg.MemSize; + buf.MemPtr = new (nothrow) char[buf.MemSize]; + + if (buf.MemPtr == nullptr) + { + return false; + } + + /* Fill the line info header */ + DwarfLineNumHeader* header = reinterpret_cast<DwarfLineNumHeader*>(buf.MemPtr.GetValue()); + memcpy(buf.MemPtr, &LineNumHeader, sizeof(DwarfLineNumHeader)); + header->m_length = buf.MemSize - sizeof(uint32_t); + header->m_hdr_length = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize - 2 * sizeof(uint32_t) - sizeof(uint16_t); + buf.MemPtr[sizeof(DwarfLineNumHeader)] = 0; // this is for missing directory table + /* copy file table */ + memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1, fileTable.MemPtr, fileTable.MemSize); + /* copy line program */ + memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize, lineProg.MemPtr, lineProg.MemSize); + + return true; +} + +/* Buid the source files table for DWARF source line info */ +bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines) +{ + const char** files = nullptr; + unsigned nfiles = 0; + + /* GetValue file names and replace them with indices in file table */ + files = new (nothrow) const char*[nlines]; + if (files == nullptr) + return false; + for (unsigned i = 0; i < nlines; ++i) + { + const char *filePath, *fileName; + SplitPathname(lines[i].fileName, filePath, fileName); + + /* if this isn't first then we already added file, so adjust index */ + lines[i].fileIndex = (nfiles) ? (nfiles - 1) : (nfiles); + + bool found = false; + for (int j = 0; j < nfiles; ++j) + { + if (strcmp(fileName, files[j]) == 0) + { + found = true; + break; + } + } + + /* add new source file */ + if (!found) + { + files[nfiles++] = fileName; + } + } + + /* build file table */ + unsigned totalSize = 0; + + for (unsigned i = 0; i < nfiles; ++i) + { + totalSize += strlen(files[i]) + 1 + 3; + } + totalSize += 1; + + buf.MemSize = totalSize; + buf.MemPtr = new (nothrow) char[buf.MemSize]; + + if (buf.MemPtr == nullptr) + { + delete[] files; + return false; + } + + /* copy collected file names */ + char *ptr = buf.MemPtr; + for (unsigned i = 0; i < nfiles; ++i) + { + strcpy(ptr, files[i]); + ptr += strlen(files[i]) + 1; + // three LEB128 entries which we don't care + *ptr++ = 0; + *ptr++ = 0; + *ptr++ = 0; + } + // final zero byte + *ptr = 0; + + delete[] files; + return true; +} + +/* Command to set absolute address */ +void NotifyGdb::IssueSetAddress(char*& ptr, PCODE addr) +{ + *ptr++ = 0; + *ptr++ = ADDRESS_SIZE + 1; + *ptr++ = DW_LNE_set_address; + *reinterpret_cast<PCODE*>(ptr) = addr; + ptr += ADDRESS_SIZE; +} + +/* End of line program */ +void NotifyGdb::IssueEndOfSequence(char*& ptr) +{ + *ptr++ = 0; + *ptr++ = 1; + *ptr++ = DW_LNE_end_sequence; +} + +/* Command w/o parameters */ +void NotifyGdb::IssueSimpleCommand(char*& ptr, uint8_t command) +{ + *ptr++ = command; +} + +/* Command with one LEB128 parameter */ +void NotifyGdb::IssueParamCommand(char*& ptr, uint8_t command, char* param, int param_size) +{ + *ptr++ = command; + while (param_size-- > 0) + { + *ptr++ = *param++; + } +} + +/* Special command moves address, line number and issue one row to source line matrix */ +void NotifyGdb::IssueSpecialCommand(char*& ptr, int8_t line_shift, uint8_t addr_shift) +{ + *ptr++ = (line_shift - DWARF_LINE_BASE) + addr_shift * DWARF_LINE_RANGE + DWARF_OPCODE_BASE; +} + +/* Check to see if given shifts are fit into one byte command */ +bool NotifyGdb::FitIntoSpecialOpcode(int8_t line_shift, uint8_t addr_shift) +{ + unsigned opcode = (line_shift - DWARF_LINE_BASE) + addr_shift * DWARF_LINE_RANGE + DWARF_OPCODE_BASE; + + return opcode < 255; +} + +/* Build program for DWARF source line section */ +bool NotifyGdb::BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines) +{ + static char cnv_buf[16]; + + /* reserve memory assuming worst case: one extended and one special command for each line */ + buf.MemSize = nlines * ( 4 + ADDRESS_SIZE) + 4; + buf.MemPtr = new (nothrow) char[buf.MemSize]; + char* ptr = buf.MemPtr; + + if (buf.MemPtr == nullptr) + return false; + + /* set absolute start address */ + IssueSetAddress(ptr, startAddr); + IssueSimpleCommand(ptr, DW_LNS_set_prologue_end); + + int prevLine = 1, prevAddr = 0, prevFile = 0; + + for (int i = 0; i < nlines; ++i) + { + /* different source file */ + if (lines[i].fileIndex != prevFile) + { + int len = Leb128Encode(static_cast<uint32_t>(lines[i].fileIndex+1), cnv_buf, sizeof(cnv_buf)); + IssueParamCommand(ptr, DW_LNS_set_file, cnv_buf, len); + prevFile = lines[i].fileIndex; + } + /* too big line number shift */ + if (lines[i].lineNumber - prevLine > (DWARF_LINE_BASE + DWARF_LINE_RANGE - 1)) + { + int len = Leb128Encode(static_cast<int32_t>(lines[i].lineNumber - prevLine), cnv_buf, sizeof(cnv_buf)); + IssueParamCommand(ptr, DW_LNS_advance_line, cnv_buf, len); + prevLine = lines[i].lineNumber; + } + /* first try special opcode */ + if (FitIntoSpecialOpcode(lines[i].lineNumber - prevLine, lines[i].nativeOffset - prevAddr)) + IssueSpecialCommand(ptr, lines[i].lineNumber - prevLine, lines[i].nativeOffset - prevAddr); + else + { + IssueSetAddress(ptr, startAddr + lines[i].nativeOffset); + IssueSpecialCommand(ptr, lines[i].lineNumber - prevLine, 0); + } + + prevLine = lines[i].lineNumber; + prevAddr = lines[i].nativeOffset; + } + + IssueEndOfSequence(ptr); + + buf.MemSize = ptr - buf.MemPtr; + return true; +} + +/* Build the DWARF .debug_str section */ +bool NotifyGdb::BuildDebugStrings(MemBuf& buf) +{ + uint32_t totalLength = 0; + + /* calculate total section size */ + for (int i = 0; i < DebugStringCount; ++i) + { + totalLength += strlen(DebugStrings[i]) + 1; + } + + buf.MemSize = totalLength; + buf.MemPtr = new (nothrow) char[totalLength]; + + if (buf.MemPtr == nullptr) + return false; + + /* copy strings */ + char* bufPtr = buf.MemPtr; + for (int i = 0; i < DebugStringCount; ++i) + { + strcpy(bufPtr, DebugStrings[i]); + bufPtr += strlen(DebugStrings[i]) + 1; + } + + return true; +} + +/* Build the DWARF .debug_abbrev section */ +bool NotifyGdb::BuildDebugAbbrev(MemBuf& buf) +{ + buf.MemPtr = new (nothrow) char[AbbrevTableSize]; + buf.MemSize = AbbrevTableSize; + + if (buf.MemPtr == nullptr) + return false; + + memcpy(buf.MemPtr, AbbrevTable, AbbrevTableSize); + return true; +} + +/* Build tge DWARF .debug_info section */ +bool NotifyGdb::BuildDebugInfo(MemBuf& buf) +{ + buf.MemSize = sizeof(DwarfCompUnit) + sizeof(DebugInfo) + 1; + buf.MemPtr = new (nothrow) char[buf.MemSize]; + + if (buf.MemPtr == nullptr) + return false; + + /* Compile uint header */ + DwarfCompUnit* cu = reinterpret_cast<DwarfCompUnit*>(buf.MemPtr.GetValue()); + cu->m_length = buf.MemSize - sizeof(uint32_t); + cu->m_version = 4; + cu->m_abbrev_offset = 0; + cu->m_addr_size = ADDRESS_SIZE; + + /* copy debug information */ + DebugInfo* di = reinterpret_cast<DebugInfo*>(buf.MemPtr + sizeof(DwarfCompUnit)); + memcpy(buf.MemPtr + sizeof(DwarfCompUnit), &debugInfo, sizeof(DebugInfo)); + di->m_prod_off = 0; + di->m_cu_name = strlen(DebugStrings[0]) + 1; + di->m_sub_name = strlen(DebugStrings[0]) + 1 + strlen(DebugStrings[1]) + 1 + strlen(DebugStrings[2]) + 1; + di->m_type_name = strlen(DebugStrings[0]) + 1 + strlen(DebugStrings[1]) + 1 + strlen(DebugStrings[2]) + 1 + strlen(DebugStrings[3]) + 1; + + /* zero end marker */ + buf.MemPtr[buf.MemSize-1] = 0; + return true; +} + +/* Build the DWARF lookup section */ +bool NotifyGdb::BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t die_offset) +{ + uint32_t length = sizeof(DwarfPubHeader) + sizeof(uint32_t) + strlen(name) + 1 + sizeof(uint32_t); + + buf.MemSize = length; + buf.MemPtr = new (nothrow) char[buf.MemSize]; + + if (buf.MemPtr == nullptr) + return false; + + DwarfPubHeader* header = reinterpret_cast<DwarfPubHeader*>(buf.MemPtr.GetValue()); + header->m_length = length - sizeof(uint32_t); + header->m_version = 2; + header->m_debug_info_off = 0; + header->m_debug_info_len = size; + *reinterpret_cast<uint32_t*>(buf.MemPtr + sizeof(DwarfPubHeader)) = die_offset; + strcpy(buf.MemPtr + sizeof(DwarfPubHeader) + sizeof(uint32_t), name); + *reinterpret_cast<uint32_t*>(buf.MemPtr + length - sizeof(uint32_t)) = 0; + + return true; +} + +/* Build ELF string section */ +bool NotifyGdb::BuildSectionNameTable(MemBuf& buf) +{ + uint32_t totalLength = 0; + + /* calculate total size */ + for (int i = 0; i < SectionNamesCount; ++i) + { + totalLength += strlen(SectionNames[i]) + 1; + } + + buf.MemSize = totalLength; + buf.MemPtr = new (nothrow) char[totalLength]; + if (buf.MemPtr == nullptr) + return false; + + /* copy strings */ + char* bufPtr = buf.MemPtr; + for (int i = 0; i < SectionNamesCount; ++i) + { + strcpy(bufPtr, SectionNames[i]); + bufPtr += strlen(SectionNames[i]) + 1; + } + + return true; +} + +/* Build the ELF section headers table */ +bool NotifyGdb::BuildSectionTable(MemBuf& buf) +{ + Elf_Shdr* sectionHeaders = new (nothrow) Elf_Shdr[SectionNamesCount - 1]; + Elf_Shdr* pSh = sectionHeaders; + + if (sectionHeaders == nullptr) + { + return false; + } + + /* NULL entry */ + pSh->sh_name = 0; + pSh->sh_type = SHT_NULL; + pSh->sh_flags = 0; + pSh->sh_addr = 0; + pSh->sh_offset = 0; + pSh->sh_size = 0; + pSh->sh_link = SHN_UNDEF; + pSh->sh_info = 0; + pSh->sh_addralign = 0; + pSh->sh_entsize = 0; + + ++pSh; + /* fill section header data */ + uint32_t sectNameOffset = 1; + for (int i = 1; i < SectionNamesCount - 1; ++i, ++pSh) + { + pSh->sh_name = sectNameOffset; + sectNameOffset += strlen(SectionNames[i]) + 1; + pSh->sh_type = Sections[i].m_type; + pSh->sh_flags = Sections[i].m_flags; + pSh->sh_addr = 0; + pSh->sh_offset = 0; + pSh->sh_size = 0; + pSh->sh_link = SHN_UNDEF; + pSh->sh_info = 0; + pSh->sh_addralign = 1; + pSh->sh_entsize = 0; + } + + buf.MemPtr = reinterpret_cast<char*>(sectionHeaders); + buf.MemSize = sizeof(Elf_Shdr) * (SectionNamesCount - 1); + return true; +} + +/* Build the ELF header */ +bool NotifyGdb::BuildELFHeader(MemBuf& buf) +{ + Elf_Ehdr* header = new (nothrow) Elf_Ehdr; + buf.MemPtr = reinterpret_cast<char*>(header); + buf.MemSize = sizeof(Elf_Ehdr); + + if (header == nullptr) + return false; + + return true; + +} + +/* Split full path name into directory & file anmes */ +void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const char*& fileName) +{ + char* pSlash = strrchr(path, '/'); + + if (pSlash != nullptr) + { + *pSlash = 0; + fileName = ++pSlash; + pathName = path; + } + else + { + fileName = path; + pathName = nullptr; + } +} + +/* LEB128 for 32-bit unsigned integer */ +int NotifyGdb::Leb128Encode(uint32_t num, char* buf, int size) +{ + int i = 0; + + do + { + uint8_t byte = num & 0x7F; + if (i >= size) + break; + num >>= 7; + if (num != 0) + byte |= 0x80; + buf[i++] = byte; + } + while (num != 0); + + return i; +} + +/* LEB128 for 32-bit signed integer */ +int NotifyGdb::Leb128Encode(int32_t num, char* buf, int size) +{ + int i = 0; + bool hasMore = true, isNegative = num < 0; + + while (hasMore && i < size) + { + uint8_t byte = num & 0x7F; + num >>= 7; + + if ((num == 0 && (byte & 0x40) == 0) || (num == -1 && (byte & 0x40) == 0x40)) + hasMore = false; + else + byte |= 0x80; + buf[i++] = byte; + } + + return i; +} + +/* ELF 32bit header */ +Elf32_Ehdr::Elf32_Ehdr() +{ + e_ident[EI_MAG0] = ElfMagic[0]; + e_ident[EI_MAG1] = ElfMagic[1]; + e_ident[EI_MAG2] = ElfMagic[2]; + e_ident[EI_MAG3] = ElfMagic[3]; + e_ident[EI_CLASS] = ELFCLASS32; + e_ident[EI_DATA] = ELFDATA2LSB; + e_ident[EI_VERSION] = EV_CURRENT; + e_ident[EI_OSABI] = ELFOSABI_NONE; + e_ident[EI_ABIVERSION] = 0; + for (int i = EI_PAD; i < EI_NIDENT; ++i) + e_ident[i] = 0; + + e_type = ET_REL; +#if defined(_TARGET_X86_) + e_machine = EM_386; +#elif defined(_TARGET_ARM_) + e_machine = EM_ARM; +#endif + e_flags = 0; + e_version = 1; + e_entry = 0; + e_phoff = 0; + e_ehsize = sizeof(Elf32_Ehdr); + e_phentsize = 0; + e_phnum = 0; +} + +/* ELF 64bit header */ +Elf64_Ehdr::Elf64_Ehdr() +{ + e_ident[EI_MAG0] = ElfMagic[0]; + e_ident[EI_MAG1] = ElfMagic[1]; + e_ident[EI_MAG2] = ElfMagic[2]; + e_ident[EI_MAG3] = ElfMagic[3]; + e_ident[EI_CLASS] = ELFCLASS64; + e_ident[EI_DATA] = ELFDATA2LSB; + e_ident[EI_VERSION] = EV_CURRENT; + e_ident[EI_OSABI] = ELFOSABI_NONE; + e_ident[EI_ABIVERSION] = 0; + for (int i = EI_PAD; i < EI_NIDENT; ++i) + e_ident[i] = 0; + + e_type = ET_REL; +#if defined(_TARGET_AMD64_) + e_machine = EM_X86_64; +#elif defined(_TARGET_ARM64_) + e_machine = EM_AARCH64; +#endif + e_flags = 0; + e_version = 1; + e_entry = 0; + e_phoff = 0; + e_ehsize = sizeof(Elf64_Ehdr); + e_phentsize = 0; + e_phnum = 0; +} diff --git a/src/vm/gdbjit.h b/src/vm/gdbjit.h new file mode 100644 index 0000000000..5c7f24b34c --- /dev/null +++ b/src/vm/gdbjit.h @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// File: gdbjit.h +// + +// +// Header file for GDB JIT interface implemenation. +// +//***************************************************************************** + + +#ifndef __GDBJIT_H__ +#define __GDBJIT_H__ + +#include <stdint.h> +#include "method.hpp" +#include "../inc/llvm/ELF.h" +#include "../inc/llvm/Dwarf.h" + +#if defined(_TARGET_X86_) || defined(_TARGET_ARM_) + typedef Elf32_Ehdr Elf_Ehdr; + typedef Elf32_Shdr Elf_Shdr; +#define ADDRESS_SIZE 4 +#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) + typedef Elf64_Ehdr Elf_Ehdr; + typedef Elf64_Shdr Elf_Shdr; +#define ADDRESS_SIZE 8 +#else +#error "Target is not supported" +#endif + +struct __attribute__((packed)) DwarfCompUnit +{ + uint32_t m_length; + uint16_t m_version; + uint32_t m_abbrev_offset; + uint8_t m_addr_size; +}; + +struct __attribute__((packed)) DwarfPubHeader +{ + uint32_t m_length; + uint16_t m_version; + uint32_t m_debug_info_off; + uint32_t m_debug_info_len; +}; + +#define DW_LNS_MAX DW_LNS_set_isa + +struct __attribute__((packed)) DwarfLineNumHeader +{ + uint32_t m_length; + uint16_t m_version; + uint32_t m_hdr_length; + uint8_t m_min_instr_len; + uint8_t m_def_is_stmt; + int8_t m_line_base; + uint8_t m_line_range; + uint8_t m_opcode_base; + uint8_t m_std_num_arg[DW_LNS_MAX]; +}; + +struct SymbolsInfo +{ + int lineNumber, ilOffset, nativeOffset, fileIndex; + char fileName[2*MAX_PATH_FNAME]; +}; + + +class NotifyGdb +{ +public: + static void MethodCompiled(MethodDesc* MethodDescPtr); + static void MethodDropped(MethodDesc* MethodDescPtr); +private: + struct MemBuf + { + NewArrayHolder<char> MemPtr; + unsigned MemSize; + MemBuf() : MemPtr(0), MemSize(0) + {} + }; + + static bool BuildELFHeader(MemBuf& buf); + static bool BuildSectionNameTable(MemBuf& buf); + static bool BuildSectionTable(MemBuf& buf); + static bool BuildDebugStrings(MemBuf& buf); + static bool BuildDebugAbbrev(MemBuf& buf); + static bool BuildDebugInfo(MemBuf& buf); + static bool BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t dieOffset); + static bool BuildLineTable(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines); + static bool BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines); + static bool BuildLineProg(MemBuf& buf, PCODE startAddr, SymbolsInfo* lines, unsigned nlines); + static bool FitIntoSpecialOpcode(int8_t line_shift, uint8_t addr_shift); + static void IssueSetAddress(char*& ptr, PCODE addr); + static void IssueEndOfSequence(char*& ptr); + static void IssueSimpleCommand(char*& ptr, uint8_t command); + static void IssueParamCommand(char*& ptr, uint8_t command, char* param, int param_len); + static void IssueSpecialCommand(char*& ptr, int8_t line_shift, uint8_t addr_shift); + static void SplitPathname(const char* path, const char*& pathName, const char*& fileName); + static int Leb128Encode(uint32_t num, char* buf, int size); + static int Leb128Encode(int32_t num, char* buf, int size); +}; + +#endif // #ifndef __GDBJIT_H__ diff --git a/src/vm/gdbjithelpers.h b/src/vm/gdbjithelpers.h new file mode 100644 index 0000000000..1fa5d4674e --- /dev/null +++ b/src/vm/gdbjithelpers.h @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** +// File: gdbjithelpers.h +// +// +// Helper file with managed delegate for GDB JIT interface implemenation. +// +//***************************************************************************** + + +#ifndef __GDBJITHELPERS_H__ +#define __GDBJITHELPERS_H__ + +struct SequencePointInfo +{ + int lineNumber, ilOffset; + char16_t* fileName; +}; + +struct MethodDebugInfo +{ + SequencePointInfo* points; + int size; +}; + +typedef int (*GetInfoForMethodDelegate)(const char*, unsigned int, MethodDebugInfo& methodDebugInfo); +extern GetInfoForMethodDelegate getInfoForMethodDelegate; + +#endif // !__GDBJITHELPERS_H__ diff --git a/src/vm/util.cpp b/src/vm/util.cpp index f7185c744f..e0f09e3ae0 100644 --- a/src/vm/util.cpp +++ b/src/vm/util.cpp @@ -3399,6 +3399,11 @@ void InitializeClrNotifications() #pragma optimize("", off) #endif // _MSC_VER +#if defined(FEATURE_GDBJIT) +#include "gdbjit.h" +__declspec(thread) bool tls_isSymReaderInProgress = false; +#endif // FEATURE_GDBJIT + // called from the runtime void DACNotify::DoJITNotification(MethodDesc *MethodDescPtr) { @@ -3410,7 +3415,14 @@ void DACNotify::DoJITNotification(MethodDesc *MethodDescPtr) MODE_PREEMPTIVE; } CONTRACTL_END; - +#if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE) + if(!tls_isSymReaderInProgress) + { + tls_isSymReaderInProgress = true; + NotifyGdb::MethodCompiled(MethodDescPtr); + tls_isSymReaderInProgress = false; + } +#endif TADDR Args[2] = { JIT_NOTIFICATION, (TADDR) MethodDescPtr }; DACNotifyExceptionHelper(Args, 2); } @@ -3426,6 +3438,9 @@ void DACNotify::DoJITDiscardNotification(MethodDesc *MethodDescPtr) } CONTRACTL_END; +#if defined(FEATURE_GDBJIT) && defined(FEATURE_PAL) && !defined(CROSSGEN_COMPILE) + NotifyGdb::MethodDropped(MethodDescPtr); +#endif TADDR Args[2] = { JIT_DISCARD_NOTIFICATION, (TADDR) MethodDescPtr }; DACNotifyExceptionHelper(Args, 2); } |