diff --git a/docs/design/datacontracts/PrecodeStubs.md b/docs/design/datacontracts/PrecodeStubs.md index 83f593fcf2c0ad..0dc4f8e1fc74c5 100644 --- a/docs/design/datacontracts/PrecodeStubs.md +++ b/docs/design/datacontracts/PrecodeStubs.md @@ -9,22 +9,32 @@ This contract provides support for examining [precode](../coreclr/botr/method-de TargetPointer GetMethodDescFromStubAddress(TargetCodePointer entryPoint); ``` -## Version 1 and 2 +## Version 1, 2, and 3 Data descriptors used: | Data Descriptor Name | Field | Meaning | | --- | --- | --- | -| PrecodeMachineDescriptor | OffsetOfPrecodeType | See `ReadPrecodeType` | -| PrecodeMachineDescriptor | ShiftOfPrecodeType | See `ReadPrecodeType` | -| PrecodeMachineDescriptor | ReadWidthOfPrecodeType | See `ReadPrecodeType` | +| PrecodeMachineDescriptor | OffsetOfPrecodeType | See `ReadPrecodeType` (Version 1 and 2 only) | +| PrecodeMachineDescriptor | ShiftOfPrecodeType | See `ReadPrecodeType` (Version 1 and 2 only) | +| PrecodeMachineDescriptor | ReadWidthOfPrecodeType | See `ReadPrecodeType` (Version 1 and 2 only) | | PrecodeMachineDescriptor | StubCodePageSize | Size of a precode code page (in bytes) | | PrecodeMachineDescriptor | CodePointerToInstrPointerMask | mask to apply to code pointers to get an address (see arm32 note) | PrecodeMachineDescriptor | StubPrecodeType | precode sort byte for stub precodes | | PrecodeMachineDescriptor | HasPInvokeImportPrecode | 1 if platform supports PInvoke precode stubs | -| PrecodeMachineDescriptor | PInvokeImportPrecodeType| precode sort byte for PInvoke precode stubs, if supported | +| PrecodeMachineDescriptor | PInvokeImportPrecodeType | precode sort byte for PInvoke precode stubs, if supported | | PrecodeMachineDescriptor | HasFixupPrecode | 1 if platform supports fixup precode stubs | -| PrecodeMachineDescriptor | FixupPrecodeType| precode sort byte for fixup precode stubs, if supported | +| PrecodeMachineDescriptor | FixupPrecodeType | precode sort byte for fixup precode stubs, if supported | | PrecodeMachineDescriptor | ThisPointerRetBufPrecodeType | precode sort byte for this pointer ret buf precodes | +| PrecodeMachineDescriptor | FixupStubPrecodeSize | Byte size of `FixupBytes` and `FixupIgnoredBytes` (Version 3 only) | +| PrecodeMachineDescriptor | FixupBytes | Assembly code of a FixupStub (Version 3 only) | +| PrecodeMachineDescriptor | FixupIgnoredBytes | Bytes to ignore of when comparing `FixupBytes` to an actual block of memory in the target process. (Version 3 only) | +| PrecodeMachineDescriptor | StubPrecodeSize | Byte size of `StubBytes` and `StubIgnoredBytes` (Version 3 only) | +| PrecodeMachineDescriptor | StubBytes | Assembly code of a StubPrecode (Version 3 only) | +| PrecodeMachineDescriptor | StubIgnoredBytes | Bytes to ignore of when comparing `StubBytes` to an actual block of memory in the target process. (Version 3 only) | +| PrecodeMachineDescriptor | FixupCodeOffset | Offset of second entrypoint into a `FixupStub` (Present in data for Version 3 and above only.) | +| PrecodeMachineDescriptor | InterpreterPrecodeType | precode sort byte for the entrypoint into the interpreter (Version 3 only) | +| PrecodeMachineDescriptor | UMEntryPrecodeType | precode sort byte for the entrypoint into the UMEntry thunk (Version 3 only) | +| PrecodeMachineDescriptor | DynamicHelperPrecodeType | precode sort byte for the entrypoint into a dynamic helper (Version 3 only) | | StubPrecodeData | MethodDesc | pointer to the MethodDesc associated with this stub precode (Version 1 only) | | StubPrecodeData | SecretParam | pointer to the MethodDesc associated with this stub precode or a second stub data pointer for other types (Version 2 only) | | StubPrecodeData | Type | precise sort of stub precode | @@ -44,12 +54,75 @@ Contracts used: | --- | | `PlatformMetadata` | -### Determining the precode type +### Determining the precode type (Version 3) +``` csharp + private bool ReadBytesAndCompare(TargetPointer instrAddress, byte[] expectedBytePattern, byte[] bytesToIgnore) + { + byte[] localCopy = new byte[expectedBytePattern.Length]; + for (int i = 0; i < expectedBytePattern.Length; i++) + { + if (bytesToIgnore[i] == 0) + { + byte targetBytePattern = _target.Read(instrAddress + i); + if (expectedBytePattern[i] != targetBytePattern) + { + return false; + } + } + } + + return true; + } + private KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrAddress) + { + KnownPrecodeType? basicPrecodeType = default; + if (ReadBytesAndCompare(instrAddress, MachineDescriptor.StubBytes, MachineDescriptor.StubIgnoredBytes)) + { + // get the actual type from the StubPrecodeData + Data.StubPrecodeData stubPrecodeData = GetStubPrecodeData(instrAddress); + byte exactPrecodeType = stubPrecodeData.Type; + if (exactPrecodeType == 0) + return null; + + if (exactPrecodeType == MachineDescriptor.StubPrecodeType) + { + return KnownPrecodeType.Stub; + } + else if (MachineDescriptor.PInvokeImportPrecodeType is byte compareByte1 && compareByte1 == exactPrecodeType) + { + return KnownPrecodeType.PInvokeImport; + } + else if (MachineDescriptor.ThisPointerRetBufPrecodeType is byte compareByte2 && compareByte2 == exactPrecodeType) + { + return KnownPrecodeType.ThisPtrRetBuf; + } + else if (MachineDescriptor.UMEntryPrecodeType is byte compareByte3 && compareByte3 == exactPrecodeType) + { + return KnownPrecodeType.UMEntry; + } + else if (MachineDescriptor.InterpreterPrecodeType is byte compareByte4 && compareByte4 == exactPrecodeType) + { + return KnownPrecodeType.Interpreter; + } + else if (MachineDescriptor.DynamicHelperPrecodeType is byte compareByte5 && compareByte5 == exactPrecodeType) + { + return KnownPrecodeType.DynamicHelper; + } + } + else if (ReadBytesAndCompare(instrAddress, MachineDescriptor.FixupBytes, MachineDescriptor.FixupIgnoredBytes)) + { + return KnownPrecodeType.Fixup; + } + return null; + } +``` + +### Determining the precode type (Version 1 and 2) An initial approximation of the precode type relies on a particular pattern at a known offset from the precode entrypoint. The precode type is expected to be encoded as an immediate. On some platforms the value is spread over multiple instruction bytes and may need to be right-shifted. -``` +```csharp private byte ReadPrecodeType(TargetPointer instrPointer) { if (MachineDescriptor.ReadWidthOfPrecodeType == 1) @@ -124,6 +197,9 @@ After the initial precode type is determined, for stub precodes a refined precod PInvokeImport, // also known as NDirectImport in the runtime Fixup, ThisPtrRetBuf, + UMEntry, + DynamicHelper, + Interpreter } internal abstract class ValidPrecode diff --git a/src/coreclr/clrdefinitions.cmake b/src/coreclr/clrdefinitions.cmake index 133d30d41e615d..89a4a650b5612f 100644 --- a/src/coreclr/clrdefinitions.cmake +++ b/src/coreclr/clrdefinitions.cmake @@ -207,6 +207,10 @@ if (FEATURE_STUBPRECODE_DYNAMIC_HELPERS) add_definitions(-DFEATURE_STUBPRECODE_DYNAMIC_HELPERS) endif() +if (FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS) + add_definitions(-DFEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS) +endif() + if (CLR_CMAKE_TARGET_APPLE) # Re-enable when dbgshim containing https://github.com/dotnet/diagnostics/pull/5487 is generally available # add_definitions(-DFEATURE_MAP_THUNKS_FROM_IMAGE) diff --git a/src/coreclr/clrfeatures.cmake b/src/coreclr/clrfeatures.cmake index b417e7c5e1d130..5cd0d5b0cd6af5 100644 --- a/src/coreclr/clrfeatures.cmake +++ b/src/coreclr/clrfeatures.cmake @@ -66,6 +66,9 @@ if (CLR_CMAKE_TARGET_WIN32) set(FEATURE_TYPEEQUIVALENCE 1) endif(CLR_CMAKE_TARGET_WIN32) +if (CLR_CMAKE_TARGET_ARCH_AMD64 OR CLR_CMAKE_TARGET_ARCH_ARM64) + set(FEATURE_STUBPRECODE_DYNAMIC_HELPERS 1) +endif() if (CLR_CMAKE_TARGET_MACCATALYST OR CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS OR CLR_CMAKE_TARGET_ARCH_WASM) set(FEATURE_CORECLR_CACHED_INTERFACE_DISPATCH 1) @@ -81,6 +84,12 @@ else() set(FEATURE_CORECLR_VIRTUAL_STUB_DISPATCH 1) endif() +# We use a flush instruction cache to protect reads from the StubPrecodeData/CallCountingStub structures in the stubs. +# This is needed because the StubPrecodeData structure is initialized after the stub code is written, and we need to ensure that +# the reads in the stub happen after the writes to the StubPrecodeData structure. We could do this with a barrier instruction in the stub, +# but that would be more expensive. +set(FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS 1) + if (CLR_CMAKE_HOST_UNIX AND CLR_CMAKE_HOST_ARCH_AMD64) # Allow 16 byte compare-exchange (cmpxchg16b) add_compile_options($<${FEATURE_CORECLR_CACHED_INTERFACE_DISPATCH}:-mcx16>) diff --git a/src/coreclr/debug/daccess/daccess.cpp b/src/coreclr/debug/daccess/daccess.cpp index 9cc51c57006eba..c1f2aa06d33a25 100644 --- a/src/coreclr/debug/daccess/daccess.cpp +++ b/src/coreclr/debug/daccess/daccess.cpp @@ -7045,6 +7045,14 @@ CLRDataCreateInstance(REFIID iid, // Release the AddRef from the QI. pClrDataAccess->Release(); } + + if (cdacInterface == nullptr) + { + // If we requested to use the cDAC, but failed to create the cDAC interface, return failure + // Release the ClrDataAccess instance we created + pClrDataAccess->Release(); + return E_FAIL; + } } } #endif diff --git a/src/coreclr/debug/daccess/enummem.cpp b/src/coreclr/debug/daccess/enummem.cpp index 4c360628edfac5..828ae77d6aae16 100644 --- a/src/coreclr/debug/daccess/enummem.cpp +++ b/src/coreclr/debug/daccess/enummem.cpp @@ -25,6 +25,8 @@ #include #endif // FEATURE_COMWRAPPERS +#include "cdacplatformmetadata.hpp" + extern HRESULT GetDacTableAddress(ICorDebugDataTarget* dataTarget, ULONG64 baseAddress, PULONG64 dacTableAddress); #if defined(DAC_MEASURE_PERF) diff --git a/src/coreclr/debug/ee/dactable.cpp b/src/coreclr/debug/ee/dactable.cpp index 80d1995acf8928..43d1f787274441 100644 --- a/src/coreclr/debug/ee/dactable.cpp +++ b/src/coreclr/debug/ee/dactable.cpp @@ -18,6 +18,7 @@ #include "../../vm/common.h" #include "../../vm/gcenv.h" #include "../../vm/ecall.h" +#include "../../vm/cdacplatformmetadata.hpp" #ifdef DEBUGGING_SUPPORTED diff --git a/src/coreclr/debug/runtimeinfo/contracts.jsonc b/src/coreclr/debug/runtimeinfo/contracts.jsonc index bcc2daae3830e3..81b28cd2e1fe92 100644 --- a/src/coreclr/debug/runtimeinfo/contracts.jsonc +++ b/src/coreclr/debug/runtimeinfo/contracts.jsonc @@ -17,7 +17,7 @@ "Loader": 1, "Object": 1, "PlatformMetadata": 1, - "PrecodeStubs": 2, + "PrecodeStubs": 3, "ReJIT": 1, "RuntimeInfo": 1, "RuntimeTypeSystem": 1, diff --git a/src/coreclr/debug/runtimeinfo/datadescriptor.h b/src/coreclr/debug/runtimeinfo/datadescriptor.h index 83e42dc3c36f85..04d6070ac6098d 100644 --- a/src/coreclr/debug/runtimeinfo/datadescriptor.h +++ b/src/coreclr/debug/runtimeinfo/datadescriptor.h @@ -523,20 +523,35 @@ CDAC_TYPE_END(MethodDescVersioningState) CDAC_TYPE_BEGIN(PrecodeMachineDescriptor) CDAC_TYPE_INDETERMINATE(PrecodeMachineDescriptor) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, ReadWidthOfPrecodeType, offsetof(PrecodeMachineDescriptor, ReadWidthOfPrecodeType)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, ShiftOfPrecodeType, offsetof(PrecodeMachineDescriptor, ShiftOfPrecodeType)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, OffsetOfPrecodeType, offsetof(PrecodeMachineDescriptor, OffsetOfPrecodeType)) CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InvalidPrecodeType, offsetof(PrecodeMachineDescriptor, InvalidPrecodeType)) -CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeType, offsetof(PrecodeMachineDescriptor, StubPrecodeType)) #ifdef HAS_NDIRECT_IMPORT_PRECODE CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, PInvokeImportPrecodeType, offsetof(PrecodeMachineDescriptor, PInvokeImportPrecodeType)) #endif + #ifdef HAS_FIXUP_PRECODE CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupPrecodeType, offsetof(PrecodeMachineDescriptor, FixupPrecodeType)) -#endif +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupCodeOffset, offsetof(PrecodeMachineDescriptor, FixupCodeOffset)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, FixupStubPrecodeSize, offsetof(PrecodeMachineDescriptor, FixupStubPrecodeSize)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupBytes, offsetof(PrecodeMachineDescriptor, FixupBytes)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, FixupIgnoredBytes, offsetof(PrecodeMachineDescriptor, FixupIgnoredBytes)) +#endif // HAS_FIXUP_PRECODE + +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeSize, offsetof(PrecodeMachineDescriptor, StubPrecodeSize)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, StubPrecodeType, offsetof(PrecodeMachineDescriptor, StubPrecodeType)) + +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubBytes, offsetof(PrecodeMachineDescriptor, StubBytes)) +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*byte[]*/, StubIgnoredBytes, offsetof(PrecodeMachineDescriptor, StubIgnoredBytes)) + #ifdef HAS_THISPTR_RETBUF_PRECODE CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, ThisPointerRetBufPrecodeType, offsetof(PrecodeMachineDescriptor, ThisPointerRetBufPrecodeType)) #endif +#ifdef FEATURE_INTERPRETER +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, InterpreterPrecodeType, offsetof(PrecodeMachineDescriptor, InterpreterPrecodeType)) +#endif +#ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, DynamicHelperPrecodeType, offsetof(PrecodeMachineDescriptor, DynamicHelperPrecodeType)) +#endif +CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint8*/, UMEntryPrecodeType, offsetof(PrecodeMachineDescriptor, UMEntryPrecodeType)) CDAC_TYPE_FIELD(PrecodeMachineDescriptor, /*uint32*/, StubCodePageSize, offsetof(PrecodeMachineDescriptor, StubCodePageSize)) CDAC_TYPE_END(PrecodeMachineDescriptor) diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 60b9f11dfbddf4..9f4af048edcee9 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -236,5 +236,7 @@ DEFINE_DACVAR(bool, dac__g_metadataUpdatesApplied, ::g_metadataUpdatesApplied) DEFINE_DACVAR(PTR_WSTR, dac__g_EntryAssemblyPath, ::g_EntryAssemblyPath) +DEFINE_DACVAR(CDacPlatformMetadata, dac__g_cdacPlatformMetadata, ::g_cdacPlatformMetadata) + #undef DEFINE_DACVAR #undef DEFINE_DACVAR_NO_DUMP diff --git a/src/coreclr/inc/executableallocator.h b/src/coreclr/inc/executableallocator.h index 973b950ad369bc..93c4758956f579 100644 --- a/src/coreclr/inc/executableallocator.h +++ b/src/coreclr/inc/executableallocator.h @@ -267,7 +267,7 @@ class ExecutableAllocator void UnmapRW(void* pRW); // Allocate thunks from a template. pTemplate is the return value from CreateTemplate - void* AllocateThunksFromTemplate(void *pTemplate, size_t templateSize); + void* AllocateThunksFromTemplate(void *pTemplate, size_t templateSize, void (*dataPageGenerator)(uint8_t* pageBase, size_t size)); // Free a set of thunks allocated from templates. pThunks must have been returned from AllocateThunksFromTemplate void FreeThunksFromTemplate(void *pThunks, size_t templateSize); diff --git a/src/coreclr/inc/loaderheap.h b/src/coreclr/inc/loaderheap.h index d3040e0b4aa448..793b30569e6b2b 100644 --- a/src/coreclr/inc/loaderheap.h +++ b/src/coreclr/inc/loaderheap.h @@ -460,9 +460,10 @@ struct InterleavedLoaderHeapConfig uint32_t StubSize; void* Template; void (*CodePageGenerator)(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size); + void (*DataPageGenerator)(uint8_t* pageBase, size_t size); }; -void InitializeLoaderHeapConfig(InterleavedLoaderHeapConfig *pConfig, size_t stubSize, void* templateInImage, void (*codePageGenerator)(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size)); +void InitializeLoaderHeapConfig(InterleavedLoaderHeapConfig *pConfig, size_t stubSize, void* templateInImage, void (*codePageGenerator)(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size), void (*dataPageGenerator)(uint8_t* pageBase, size_t size)); //=============================================================================== // This is the base class for InterleavedLoaderHeap It's used as a simple diff --git a/src/coreclr/inc/volatile.h b/src/coreclr/inc/volatile.h index ce49c38ef7ac22..5f186352044000 100644 --- a/src/coreclr/inc/volatile.h +++ b/src/coreclr/inc/volatile.h @@ -73,6 +73,11 @@ #endif #if defined(__GNUC__) +#if defined(HOST_X86) || defined(HOST_AMD64) +#define SFENCE_MEMORY_BARRIER() asm volatile ("sfence" : : : "memory") +#else +#define SFENCE_MEMORY_BARRIER() +#endif #if defined(HOST_ARMV6) // DMB ISH not valid on ARMv6 #define VOLATILE_MEMORY_BARRIER() asm volatile ("mcr p15, 0, r0, c7, c10, 5" : : : "memory") @@ -97,13 +102,22 @@ // #define VOLATILE_MEMORY_BARRIER() asm volatile ("" : : : "memory") #endif // HOST_ARM || HOST_ARM64 + #elif (defined(HOST_ARM) || defined(HOST_ARM64)) && _ISO_VOLATILE // ARM & ARM64 have a very weak memory model and very few tools to control that model. We're forced to perform a full // memory barrier to preserve the volatile semantics. Technically this is only necessary on MP systems but we // currently don't have a cheap way to determine the number of CPUs from this header file. Revisit this if it // turns out to be a performance issue for the uni-proc case. + #define VOLATILE_MEMORY_BARRIER() MemoryBarrier() +#define SFENCE_MEMORY_BARRIER() #else + +#if defined(HOST_X86) || defined(HOST_AMD64) +#define SFENCE_MEMORY_BARRIER() _mm_sfence() +#else +#define SFENCE_MEMORY_BARRIER() +#endif // // On VC++, reorderings at the compiler and machine level are prevented by the use of the // "volatile" keyword in VolatileLoad and VolatileStore. This should work on any CPU architecture diff --git a/src/coreclr/minipal/Unix/doublemapping.cpp b/src/coreclr/minipal/Unix/doublemapping.cpp index 4a2516bea58484..47a91c6243d9c0 100644 --- a/src/coreclr/minipal/Unix/doublemapping.cpp +++ b/src/coreclr/minipal/Unix/doublemapping.cpp @@ -482,7 +482,7 @@ void* VMToOSInterface::CreateTemplate(void* pImageTemplate, size_t templateSize, #endif } -void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStartSpecification) +void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStartSpecification, void (*dataPageGenerator)(uint8_t* pageBase, size_t size)) { #ifdef TARGET_APPLE vm_address_t addr, taddr; @@ -501,6 +501,12 @@ void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templa return NULL; } + if (dataPageGenerator) + { + // Generate the data page before we map the code page into memory + dataPageGenerator(((uint8_t*) addr) + templateSize, templateSize); + } + do { ret = vm_remap( @@ -549,6 +555,12 @@ void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templa return NULL; } + if (dataPageGenerator) + { + // Generate the data page before we map the code page into memory + dataPageGenerator(((uint8_t*) pStart) + templateSize, templateSize); + } + void *pStartCode = mmap(pStart, templateSize, PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_FIXED, pThunkData->fdImage, fileOffset); if (pStart != pStartCode) { diff --git a/src/coreclr/minipal/Windows/doublemapping.cpp b/src/coreclr/minipal/Windows/doublemapping.cpp index 0620a80477027d..59b8e0f11fa04c 100644 --- a/src/coreclr/minipal/Windows/doublemapping.cpp +++ b/src/coreclr/minipal/Windows/doublemapping.cpp @@ -225,7 +225,7 @@ bool VMToOSInterface::AllocateThunksFromTemplateRespectsStartAddress() return false; } -void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStart) +void* VMToOSInterface::AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStart, void (*dataPageGenerator)(uint8_t* pageBase, size_t size)) { return NULL; } diff --git a/src/coreclr/minipal/minipal.h b/src/coreclr/minipal/minipal.h index 01f497e60e6d7e..6c99c29c8c6ce2 100644 --- a/src/coreclr/minipal/minipal.h +++ b/src/coreclr/minipal/minipal.h @@ -97,11 +97,12 @@ class VMToOSInterface // pTemplate - Value returned from CreateTemplate // templateSize - Size of the templates block in the image // pStart - Where to allocate (Specify NULL if no particular address is required). If non-null, this must be an address returned by ReserveDoubleMappedMemory + // dataPageGenerator - If non-null fill the data page of the template using this function. This function is called BEFORE the code page is mapped into memory. // // Return: // NULL if the allocation fails // Non-NULL, a pointer to the allocated region. - static void* AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStart); + static void* AllocateThunksFromTemplate(void* pTemplate, size_t templateSize, void* pStart, void (*dataPageGenerator)(uint8_t* pageBase, size_t size)); // Free thunks allocated from template // Parameters: diff --git a/src/coreclr/utilcode/executableallocator.cpp b/src/coreclr/utilcode/executableallocator.cpp index 0242377072238c..4cacac8526b187 100644 --- a/src/coreclr/utilcode/executableallocator.cpp +++ b/src/coreclr/utilcode/executableallocator.cpp @@ -978,7 +978,7 @@ void ExecutableAllocator::UnmapRW(void* pRW) } } -void* ExecutableAllocator::AllocateThunksFromTemplate(void *pTemplate, size_t templateSize) +void* ExecutableAllocator::AllocateThunksFromTemplate(void *pTemplate, size_t templateSize, void (*dataPageGenerator)(uint8_t* pageBase, size_t size)) { if (IsDoubleMappingEnabled() && VMToOSInterface::AllocateThunksFromTemplateRespectsStartAddress()) { @@ -1003,7 +1003,7 @@ void* ExecutableAllocator::AllocateThunksFromTemplate(void *pTemplate, size_t te BackoutBlock(block, isFreeBlock); } - void *pTemplateAddressAllocated = VMToOSInterface::AllocateThunksFromTemplate(pTemplate, templateSize, block->baseRX); + void *pTemplateAddressAllocated = VMToOSInterface::AllocateThunksFromTemplate(pTemplate, templateSize, block->baseRX, dataPageGenerator); if (pTemplateAddressAllocated == NULL) { @@ -1014,7 +1014,7 @@ void* ExecutableAllocator::AllocateThunksFromTemplate(void *pTemplate, size_t te } else { - return VMToOSInterface::AllocateThunksFromTemplate(pTemplate, templateSize, NULL); + return VMToOSInterface::AllocateThunksFromTemplate(pTemplate, templateSize, NULL, dataPageGenerator); } } diff --git a/src/coreclr/utilcode/interleavedloaderheap.cpp b/src/coreclr/utilcode/interleavedloaderheap.cpp index 082e337caebda1..ec39c40d83f3db 100644 --- a/src/coreclr/utilcode/interleavedloaderheap.cpp +++ b/src/coreclr/utilcode/interleavedloaderheap.cpp @@ -104,25 +104,32 @@ size_t UnlockedInterleavedLoaderHeap::GetBytesAvailReservedRegion() BOOL UnlockedInterleavedLoaderHeap::CommitPages(void* pData, size_t dwSizeToCommitPart) { _ASSERTE(m_pConfig->Template == NULL); // This path should only be used for LoaderHeaps which use the standard ExecutableAllocator functions - // Commit first set of pages, since it will contain the LoaderHeapBlock + { - void *pTemp = ExecutableAllocator::Instance()->Commit(pData, dwSizeToCommitPart, IsExecutable()); + void *pTemp = ExecutableAllocator::Instance()->Commit((BYTE*)pData + dwSizeToCommitPart, dwSizeToCommitPart, FALSE); if (pTemp == NULL) { return FALSE; } + // Fill in data pages with the initial state, do this before we map the executable pages in, so that + // the executable pages cannot speculate into the data page at any time before they are initialized. + if (m_pConfig->DataPageGenerator != NULL) + { + m_pConfig->DataPageGenerator((uint8_t*)pTemp, dwSizeToCommitPart); + } } - _ASSERTE(dwSizeToCommitPart == GetStubCodePageSize()); - + // Commit first set of pages, since it will contain the LoaderHeapBlock { - void *pTemp = ExecutableAllocator::Instance()->Commit((BYTE*)pData + dwSizeToCommitPart, dwSizeToCommitPart, FALSE); + void *pTemp = ExecutableAllocator::Instance()->Commit(pData, dwSizeToCommitPart, IsExecutable()); if (pTemp == NULL) { return FALSE; } } + _ASSERTE(dwSizeToCommitPart == GetStubCodePageSize()); + ExecutableWriterHolder codePageWriterHolder((BYTE*)pData, dwSizeToCommitPart, ExecutableAllocator::DoNotAddToCache); m_pConfig->CodePageGenerator(codePageWriterHolder.GetRW(), (BYTE*)pData, dwSizeToCommitPart); FlushInstructionCache(GetCurrentProcess(), pData, dwSizeToCommitPart); @@ -252,7 +259,7 @@ BOOL UnlockedInterleavedLoaderHeap::GetMoreCommittedPages(size_t dwMinSize) if (m_pConfig->Template != NULL) { - ThunkMemoryHolder newAllocatedThunks = (BYTE*)ExecutableAllocator::Instance()->AllocateThunksFromTemplate(m_pConfig->Template, GetStubCodePageSize()); + ThunkMemoryHolder newAllocatedThunks = (BYTE*)ExecutableAllocator::Instance()->AllocateThunksFromTemplate(m_pConfig->Template, GetStubCodePageSize(), m_pConfig->DataPageGenerator); if (newAllocatedThunks == NULL) { return FALSE; @@ -490,11 +497,14 @@ void *UnlockedInterleavedLoaderHeap::UnlockedAllocStub_NoThrow( } #ifdef _DEBUG - // Check to ensure that the RW region of the allocated stub is zeroed out - BYTE *pAllocatedRWBytes = (BYTE*)pResult + GetStubCodePageSize(); - for (size_t i = 0; i < dwRequestedSize; i++) + // Check to ensure that the RW region of the allocated stub is zeroed out if there isn't a data page generator + if (m_pConfig->DataPageGenerator == NULL) { - _ASSERTE_MSG(pAllocatedRWBytes[i] == 0, "LoaderHeap must return zero-initialized memory"); + BYTE *pAllocatedRWBytes = (BYTE*)pResult + GetStubCodePageSize(); + for (size_t i = 0; i < dwRequestedSize; i++) + { + _ASSERTE_MSG(pAllocatedRWBytes[i] == 0, "LoaderHeap must return zero-initialized memory"); + } } if (m_dwDebugFlags & kCallTracing) @@ -539,11 +549,12 @@ void *UnlockedInterleavedLoaderHeap::UnlockedAllocStub( return pResult; } -void InitializeLoaderHeapConfig(InterleavedLoaderHeapConfig *pConfig, size_t stubSize, void* templateInImage, void (*codePageGenerator)(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size)) +void InitializeLoaderHeapConfig(InterleavedLoaderHeapConfig *pConfig, size_t stubSize, void* templateInImage, void (*codePageGenerator)(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size), void (*dataPageGenerator)(uint8_t* pageBase, size_t size)) { pConfig->StubSize = (uint32_t)stubSize; pConfig->Template = ExecutableAllocator::Instance()->CreateTemplate(templateInImage, GetStubCodePageSize(), codePageGenerator); pConfig->CodePageGenerator = codePageGenerator; + pConfig->DataPageGenerator = dataPageGenerator; } #endif // #ifndef DACCESS_COMPILE diff --git a/src/coreclr/vm/arm/thunktemplates.S b/src/coreclr/vm/arm/thunktemplates.S index b535090660a612..862c1cb2d34def 100644 --- a/src/coreclr/vm/arm/thunktemplates.S +++ b/src/coreclr/vm/arm/thunktemplates.S @@ -14,6 +14,7 @@ PAGE_SIZE = 4096 #define DATA_SLOT(stub, field) . - (. - stub##Code) + PAGE_SIZE + stub##Data__##field LEAF_ENTRY StubPrecodeCode + dmb ldr r12, DATA_SLOT(StubPrecode, SecretParam) ldr pc, DATA_SLOT(StubPrecode, Target) LEAF_END_MARKED StubPrecodeCode @@ -22,6 +23,7 @@ PAGE_SIZE = 4096 LEAF_ENTRY FixupPrecodeCode ldr pc, DATA_SLOT(FixupPrecode, Target) + dmb ldr r12, DATA_SLOT(FixupPrecode, MethodDesc) ldr pc, DATA_SLOT(FixupPrecode, PrecodeFixupThunk) LEAF_END_MARKED FixupPrecodeCode @@ -29,6 +31,7 @@ PAGE_SIZE = 4096 .align 4 LEAF_ENTRY CallCountingStubCode + dmb push {r0} ldr r12, DATA_SLOT(CallCountingStub, RemainingCallCountCell) ldrh r0, [r12] diff --git a/src/coreclr/vm/arm64/thunktemplates.S b/src/coreclr/vm/arm64/thunktemplates.S index bbbc490854721e..6320513b73e4c3 100644 --- a/src/coreclr/vm/arm64/thunktemplates.S +++ b/src/coreclr/vm/arm64/thunktemplates.S @@ -52,7 +52,7 @@ LEAF_END_MARKED StubPrecodeCodeTemplate, _TEXT // FixupPrecode // ---------- -#define FIXUP_PRECODE_CODESIZE 0x18 // 5 instructions, 4 bytes each (and we also have 4 bytes of padding) +#define FIXUP_PRECODE_CODESIZE 0x18 // 6 instructions, 4 bytes each #define FIXUP_PRECODE_DATASIZE 0x18 // 3 qwords .set FIXUP_PRECODE_NUM_THUNKS_PER_MAPPING,(THUNKS_MAP_SIZE / FIXUP_PRECODE_CODESIZE) @@ -62,10 +62,10 @@ LEAF_END_MARKED StubPrecodeCodeTemplate, _TEXT ldr x11, DATA_SLOT(FixupPrecode, Target, FIXUP_PRECODE_CODESIZE, FixupPrecodeCodeTemplate) br x11 + dmb ishld ldr x12, DATA_SLOT(FixupPrecode, MethodDesc, FIXUP_PRECODE_CODESIZE, FixupPrecodeCodeTemplate) ldr x11, DATA_SLOT(FixupPrecode, PrecodeFixupThunk, FIXUP_PRECODE_CODESIZE, FixupPrecodeCodeTemplate) br x11 - brk 0xf000 // Stubs need to be 24-byte in size to allow for the data to be 3 pointers IN_PAGE_INDEX = IN_PAGE_INDEX + 1 .endr @@ -81,7 +81,7 @@ LEAF_END_MARKED FixupPrecodeCodeTemplate, _TEXT // CallCountingStub // ---------- -#define CALLCOUNTING_CODESIZE 0x28 // 5 instructions, 4 bytes each (and we also have 4 bytes of padding) +#define CALLCOUNTING_CODESIZE 0x28 // 10 instructions, 4 bytes each #define CALLCOUNTING_DATASIZE 0x18 // 3 qwords .set CALLCOUNTING_NUM_THUNKS_PER_MAPPING, (THUNKS_MAP_SIZE / CALLCOUNTING_CODESIZE) @@ -99,7 +99,7 @@ LEAF_END_MARKED FixupPrecodeCodeTemplate, _TEXT 0: ldr x10, DATA_SLOT(CallCountingStub, TargetForThresholdReached, CALLCOUNTING_CODESIZE, CallCountingStubCodeTemplate) br x10 - brk 0xf000 // Stubs need to be 40-byte in size to allow for the data to be pointer aligned + brk 0xf000 // Stubs need to be aligned IN_PAGE_INDEX = IN_PAGE_INDEX + 1 .endr @@ -123,11 +123,15 @@ LEAF_END_MARKED CallCountingStubCodeTemplate, _TEXT ldr x10, DATA_SLOT(StubPrecode, Target) ldr x12, DATA_SLOT(StubPrecode, SecretParam) br x10 + brk 0xf000 // Stubs need to be 24-byte in size to allow for the data to be 3 pointers + brk 0xf000 // Stubs need to be 24-byte in size to allow for the data to be 3 pointers + brk 0xf000 // Stubs need to be 24-byte in size to allow for the data to be 3 pointers LEAF_END_MARKED StubPrecodeCode\STUB_PAGE_SIZE LEAF_ENTRY FixupPrecodeCode\STUB_PAGE_SIZE ldr x11, DATA_SLOT(FixupPrecode, Target) br x11 + dmb ishld ldr x12, DATA_SLOT(FixupPrecode, MethodDesc) ldr x11, DATA_SLOT(FixupPrecode, PrecodeFixupThunk) br x11 @@ -145,6 +149,7 @@ LOCAL_LABEL(StubStart\STUB_PAGE_SIZE): LOCAL_LABEL(CountReachedZero\STUB_PAGE_SIZE): ldr x10, DATA_SLOT(CallCountingStub, TargetForThresholdReached) br x10 + brk 0xf000 // Stubs need to be aligned in total size LEAF_END_MARKED CallCountingStubCode\STUB_PAGE_SIZE .endr diff --git a/src/coreclr/vm/callcounting.cpp b/src/coreclr/vm/callcounting.cpp index dd2c912921b82d..037f966864cb09 100644 --- a/src/coreclr/vm/callcounting.cpp +++ b/src/coreclr/vm/callcounting.cpp @@ -263,6 +263,15 @@ const CallCountingStub *CallCountingManager::CallCountingStubAllocator::Allocate allocationAddressHolder.SuppressRelease(); stub->Initialize(targetForMethod, remainingCallCountCell); +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(stub, CallCountingStub::CodeSize); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + return stub; } @@ -328,7 +337,7 @@ void CallCountingStub::StaticInitialize() _ASSERTE((SIZE_T)((BYTE*)CallCountingStubCode_End - (BYTE*)CallCountingStubCode) <= CallCountingStub::CodeSize); #endif - InitializeLoaderHeapConfig(&s_callCountingHeapConfig, CallCountingStub::CodeSize, (void*)CallCountingStubCodeTemplate, CallCountingStub::GenerateCodePage); + InitializeLoaderHeapConfig(&s_callCountingHeapConfig, CallCountingStub::CodeSize, (void*)CallCountingStubCodeTemplate, CallCountingStub::GenerateCodePage, NULL); } #endif // DACCESS_COMPILE diff --git a/src/coreclr/vm/cdacplatformmetadata.cpp b/src/coreclr/vm/cdacplatformmetadata.cpp index 0c9c1233e94870..e616d10c82afde 100644 --- a/src/coreclr/vm/cdacplatformmetadata.cpp +++ b/src/coreclr/vm/cdacplatformmetadata.cpp @@ -3,20 +3,24 @@ #include "cdacplatformmetadata.hpp" -#ifndef DACCESS_COMPILE -CDacPlatformMetadata g_cdacPlatformMetadata; +GVAL_IMPL(CDacPlatformMetadata, g_cdacPlatformMetadata); +#ifndef DACCESS_COMPILE void CDacPlatformMetadata::Init() { - PrecodeMachineDescriptor::Init(&g_cdacPlatformMetadata.precode); #if defined(TARGET_ARM) - g_cdacPlatformMetadata.codePointerFlags = CDacCodePointerFlags::HasArm32ThumbBit; + (&g_cdacPlatformMetadata)->codePointerFlags = CDacCodePointerFlags::HasArm32ThumbBit; #elif defined(TARGET_ARM64) && defined(TARGET_APPLE) // TODO set HasArm64PtrAuth if arm64e - g_cdacPlatformMetadata.codePointerFlags = CDacCodePointerFlags::None; + (&g_cdacPlatformMetadata)->codePointerFlags = CDacCodePointerFlags::None; #else - g_cdacPlatformMetadata.codePointerFlags = CDacCodePointerFlags::None; + (&g_cdacPlatformMetadata)->codePointerFlags = CDacCodePointerFlags::None; #endif } +void CDacPlatformMetadata::InitPrecodes() +{ + PrecodeMachineDescriptor::Init(&(&g_cdacPlatformMetadata)->precode); +} + #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/cdacplatformmetadata.hpp b/src/coreclr/vm/cdacplatformmetadata.hpp index 0ee94acab1a1ef..e89ccc540a3265 100644 --- a/src/coreclr/vm/cdacplatformmetadata.hpp +++ b/src/coreclr/vm/cdacplatformmetadata.hpp @@ -7,7 +7,6 @@ #include "precode.h" // Cross-cutting metadata for cDAC -#ifndef DACCESS_COMPILE enum class CDacCodePointerFlags : uint8_t { None = 0, @@ -24,10 +23,10 @@ struct CDacPlatformMetadata CDacPlatformMetadata(const CDacPlatformMetadata&) = delete; CDacPlatformMetadata& operator=(const CDacPlatformMetadata&) = delete; static void Init(); + static void InitPrecodes(); }; -extern CDacPlatformMetadata g_cdacPlatformMetadata; -#endif // DACCESS_COMPILE +GVAL_DECL(CDacPlatformMetadata, g_cdacPlatformMetadata); #endif// CDACPLATFORMMETADATA_HPP__ diff --git a/src/coreclr/vm/ceemain.cpp b/src/coreclr/vm/ceemain.cpp index 709b35510867c7..a632fc9e864d5d 100644 --- a/src/coreclr/vm/ceemain.cpp +++ b/src/coreclr/vm/ceemain.cpp @@ -804,6 +804,7 @@ void EEStartupHelper() StubLinkerCPU::Init(); StubPrecode::StaticInitialize(); FixupPrecode::StaticInitialize(); + CDacPlatformMetadata::InitPrecodes(); InitializeGarbageCollector(); diff --git a/src/coreclr/vm/dllimportcallback.cpp b/src/coreclr/vm/dllimportcallback.cpp index 405da3b43d8e25..a84e7be4bd7c3a 100644 --- a/src/coreclr/vm/dllimportcallback.cpp +++ b/src/coreclr/vm/dllimportcallback.cpp @@ -289,6 +289,15 @@ void UMEntryThunkData::Terminate() // TheUMEntryPrestub includes diagnostic for collected delegates m_pUMEntryThunk->SetTargetUnconditional(TheUMThunkPreStub()); +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(m_pUMEntryThunk, sizeof(m_pUMEntryThunk), true); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + OBJECTHANDLE pObjectHandle = m_pObjectHandle; // Set m_pObjectHandle indicate the collected state diff --git a/src/coreclr/vm/dllimportcallback.h b/src/coreclr/vm/dllimportcallback.h index b79d066c0b5470..07aa139e261a2f 100644 --- a/src/coreclr/vm/dllimportcallback.h +++ b/src/coreclr/vm/dllimportcallback.h @@ -208,6 +208,15 @@ class UMEntryThunkData #ifdef _DEBUG m_state = kLoadTimeInited; #endif + +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(m_pUMEntryThunk, sizeof(m_pUMEntryThunk)); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS } void Terminate(); diff --git a/src/coreclr/vm/loaderallocator.hpp b/src/coreclr/vm/loaderallocator.hpp index 58d2431e3b07a5..3b7e0833fa883b 100644 --- a/src/coreclr/vm/loaderallocator.hpp +++ b/src/coreclr/vm/loaderallocator.hpp @@ -625,7 +625,7 @@ class LoaderAllocator } #if defined(FEATURE_READYTORUN) && defined(FEATURE_STUBPRECODE_DYNAMIC_HELPERS) - PTR_LoaderHeap GetDynamicHelpersStubHeap() + PTR_InterleavedLoaderHeap GetDynamicHelpersStubHeap() { LIMITED_METHOD_CONTRACT; return m_pDynamicHelpersStubHeap; diff --git a/src/coreclr/vm/loongarch64/thunktemplates.S b/src/coreclr/vm/loongarch64/thunktemplates.S index 7deab2b6eadf29..52be9666d29582 100644 --- a/src/coreclr/vm/loongarch64/thunktemplates.S +++ b/src/coreclr/vm/loongarch64/thunktemplates.S @@ -7,19 +7,20 @@ // Note that the offsets specified in pcaddi must match the behavior of GetStubCodePageSize() on this architecture/os. LEAF_ENTRY StubPrecodeCode - pcaddi $r21, 0x1004 //4, for Type encoding. - ld.d $t2,$r21, (StubPrecodeData__SecretParam - 4*4) - ld.d $r21,$r21, (StubPrecodeData__Target - 4*4) + pcaddi $r21, 0x1000 + ld.d $t2,$r21, StubPrecodeData__SecretParam + ld.d $r21,$r21, StubPrecodeData__Target jirl $r0,$r21,0 LEAF_END_MARKED StubPrecodeCode LEAF_ENTRY FixupPrecodeCode - pcaddi $r21, 0x1003 //3, for Type encoding. - ld.d $r21,$r21, (FixupPrecodeData__Target - 4*3) + pcaddi $r21, 0x1000 + ld.d $r21,$r21, FixupPrecodeData__Target jirl $r0,$r21,0 - pcaddi $r21, 0x1003 - ld.d $t2,$r21, (FixupPrecodeData__MethodDesc - 4*3 -4*3) - ld.d $r21,$r21, (FixupPrecodeData__PrecodeFixupThunk - 4*3 - 4*3) + pcaddi $r21, 0xFFD + dbar 0 + ld.d $t2,$r21, FixupPrecodeData__MethodDesc + ld.d $r21,$r21, FixupPrecodeData__PrecodeFixupThunk jirl $r0,$r21,0 LEAF_END_MARKED FixupPrecodeCode diff --git a/src/coreclr/vm/methoddescbackpatchinfo.cpp b/src/coreclr/vm/methoddescbackpatchinfo.cpp index c1b0283169e197..3e76850bd2b4b5 100644 --- a/src/coreclr/vm/methoddescbackpatchinfo.cpp +++ b/src/coreclr/vm/methoddescbackpatchinfo.cpp @@ -28,17 +28,17 @@ void EntryPointSlots::Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entr switch (slotType) { case SlotType_Normal: - *(PCODE *)slot = entryPoint; + VolatileStore((PCODE *)slot, entryPoint); break; case SlotType_Vtable: - *((MethodTable::VTableIndir2_t *)slot) = entryPoint; + VolatileStore(((MethodTable::VTableIndir2_t *)slot), entryPoint); break; case SlotType_Executable: { ExecutableWriterHolder slotWriterHolder((void*)slot, sizeof(PCODE*)); - *(PCODE *)slotWriterHolder.GetRW() = entryPoint; + VolatileStore((PCODE *)slotWriterHolder.GetRW(), entryPoint); goto Flush; } @@ -48,7 +48,7 @@ void EntryPointSlots::Backpatch_Locked(TADDR slot, SlotType slotType, PCODE entr _ASSERTE(sizeof(void *) <= 4); ExecutableWriterHolder slotWriterHolder((void*)slot, sizeof(PCODE*)); - *(PCODE *)slotWriterHolder.GetRW() = entryPoint - ((PCODE)slot + sizeof(PCODE)); + VolatileStore((PCODE *)slotWriterHolder.GetRW(), entryPoint - ((PCODE)slot + sizeof(PCODE))); // fall through } @@ -76,14 +76,28 @@ void MethodDescBackpatchInfoTracker::Backpatch_Locked(MethodDesc *pMethodDesc, P WRAPPER_NO_CONTRACT; _ASSERTE(IsLockOwnedByCurrentThread()); _ASSERTE(pMethodDesc != nullptr); + bool fReadyToPatchExecutableCode = false; - auto lambda = [&entryPoint](LoaderAllocator *pLoaderAllocatorOfSlot, MethodDesc *pMethodDesc, UINT_PTR slotData) + auto lambda = [&entryPoint, &fReadyToPatchExecutableCode](LoaderAllocator *pLoaderAllocatorOfSlot, MethodDesc *pMethodDesc, UINT_PTR slotData) { TADDR slot; EntryPointSlots::SlotType slotType; EntryPointSlots::ConvertUINT_PTRToSlotAndTypePair(slotData, &slot, &slotType); + if (!fReadyToPatchExecutableCode && ((slotType == EntryPointSlots::SlotType_Executable) || slotType == EntryPointSlots::SlotType_ExecutableRel32)) + { + // We need to patch the executable code, so we need to make absolutely sure that all writes to the entry point contents are written before + // we patch the executable code. We only need to do this once, and it isn't equivalent to VolatileStore, as our definition of VolatileStore + // doesn't include a means to drain the store queue before the patch. The important detail is that executing the instruction stream is not + // logically equivalent to performing a memory load, and does not participate in all of the same load/store ordering guarantees. + // Intel does not provide a precise definition of this, but it does describe the situation as "cross modifying code", and describes an unforunately + // impractical scheme involving running cpuid on all cores that might execute the code in question. Since both the old/new code are semantically + // equivalent, we're going to use an sfence to ensure that at least all the writes to establish the new code are completely visible to all cores + // before we actually patch the executable code. + SFENCE_MEMORY_BARRIER(); + fReadyToPatchExecutableCode = true; + } EntryPointSlots::Backpatch_Locked(slot, slotType, entryPoint); return true; // Keep walking diff --git a/src/coreclr/vm/precode.cpp b/src/coreclr/vm/precode.cpp index 798e9849de3a6a..5e8e7e0efba030 100644 --- a/src/coreclr/vm/precode.cpp +++ b/src/coreclr/vm/precode.cpp @@ -229,6 +229,16 @@ InterpreterPrecode* Precode::AllocateInterpreterPrecode(PCODE byteCode, InterpreterPrecode* pPrecode = (InterpreterPrecode*)pamTracker->Track(pLoaderAllocator->GetNewStubPrecodeHeap()->AllocStub()); pPrecode->Init(pPrecode, byteCode); + +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(pPrecode, sizeof(InterpreterPrecode)); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + #ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "UMEntryThunk", (PCODE)pPrecode, sizeof(InterpreterPrecode), PerfMapStubType::IndividualWithinBlock); #endif @@ -265,6 +275,16 @@ Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, ThisPtrRetBufPrecodeData *pData = (ThisPtrRetBufPrecodeData*)pamTracker->Track(pLoaderAllocator->GetLowFrequencyHeap()->AllocMem(S_SIZE_T(sizeof(ThisPtrRetBufPrecodeData)))); pThisPtrRetBufPrecode->Init(pData, pMD, pLoaderAllocator); pPrecode = (Precode*)pThisPtrRetBufPrecode; + +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(pPrecode, sizeof(ThisPtrRetBufPrecode)); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + #ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "ThisPtrRetBuf", (PCODE)pPrecode, sizeof(ThisPtrRetBufPrecodeData), PerfMapStubType::IndividualWithinBlock); #endif @@ -275,6 +295,16 @@ Precode* Precode::Allocate(PrecodeType t, MethodDesc* pMD, _ASSERTE(t == PRECODE_STUB || t == PRECODE_NDIRECT_IMPORT); pPrecode = (Precode*)pamTracker->Track(pLoaderAllocator->GetNewStubPrecodeHeap()->AllocStub()); pPrecode->Init(pPrecode, t, pMD, pLoaderAllocator); + +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(pPrecode, sizeof(StubPrecode)); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + #ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, t == PRECODE_STUB ? "StubPrecode" : "PInvokeImportPrecode", (PCODE)pPrecode, sizeof(StubPrecode), PerfMapStubType::IndividualWithinBlock); #endif @@ -496,8 +526,8 @@ extern "C" size_t StubPrecodeCode_Target_Offset; #endif #if defined(TARGET_ARM64) && defined(TARGET_UNIX) -void (*StubPrecode::StubPrecodeCode)(); -void (*StubPrecode::StubPrecodeCode_End)(); +static void (*StubPrecodeCode)(); +static void (*StubPrecodeCode_End)(); #endif #ifdef FEATURE_MAP_THUNKS_FROM_IMAGE @@ -534,21 +564,20 @@ void StubPrecode::StaticInitialize() #else _ASSERTE((SIZE_T)((BYTE*)StubPrecodeCode_End - (BYTE*)StubPrecodeCode) <= StubPrecode::CodeSize); #endif -#ifdef TARGET_LOONGARCH64 - _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type); -#elif TARGET_RISCV64 - _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == StubPrecode::Type); -#else - _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == StubPrecode::Type); -#endif + _ASSERTE(IsStubPrecodeByASM_DAC((PCODE)StubPrecodeCode)); + if (StubPrecodeCodeTemplate != NULL) + { + _ASSERTE(IsStubPrecodeByASM_DAC((PCODE)StubPrecodeCodeTemplate)); + } - InitializeLoaderHeapConfig(&s_stubPrecodeHeapConfig, StubPrecode::CodeSize, (void*)StubPrecodeCodeTemplate, StubPrecode::GenerateCodePage); + InitializeLoaderHeapConfig(&s_stubPrecodeHeapConfig, StubPrecode::CodeSize, (void*)StubPrecodeCodeTemplate, StubPrecode::GenerateCodePage, NULL); } void StubPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { +#ifndef TARGET_WASM + int totalCodeSize = (int)(pageSize / StubPrecode::CodeSize) * StubPrecode::CodeSize; #ifdef TARGET_X86 - int totalCodeSize = (pageSize / StubPrecode::CodeSize) * StubPrecode::CodeSize; for (int i = 0; i < totalCodeSize; i += StubPrecode::CodeSize) { memcpy(pageBase + i, (const void*)StubPrecodeCode, (uint8_t*)StubPrecodeCode_End - (uint8_t*)StubPrecodeCode); @@ -562,6 +591,14 @@ void StubPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_ #else // TARGET_X86 FillStubCodePage(pageBase, (const void*)PCODEToPINSTR((PCODE)StubPrecodeCode), StubPrecode::CodeSize, pageSize); #endif // TARGET_X86 +#ifdef _DEBUG + for (int i = 0; i < totalCodeSize; i += StubPrecode::CodeSize) + { + _ASSERTE(StubPrecode::IsStubPrecodeByASM((PCODE)(pageBaseRX + i))); + _ASSERTE(StubPrecode::IsStubPrecodeByASM_DAC((PCODE)(pageBaseRX + i))); + } +#endif // _DEBUG +#endif // TARGET_WASM } BOOL StubPrecode::IsStubPrecodeByASM(PCODE addr) @@ -619,8 +656,8 @@ void FixupPrecode::Init(FixupPrecode* pPrecodeRX, MethodDesc* pMD, LoaderAllocat _ASSERTE(GetMethodDesc() == (TADDR)pMD); - pData->Target = (PCODE)pPrecodeRX + FixupPrecode::FixupCodeOffset; pData->PrecodeFixupThunk = GetPreStubEntryPoint(); + VolatileStore(&pData->Target, (PCODE)pPrecodeRX + FixupPrecode::FixupCodeOffset); } #if defined(TARGET_ARM64) && defined(TARGET_UNIX) @@ -641,8 +678,8 @@ extern "C" size_t FixupPrecodeCode_PrecodeFixupThunk_Offset; #endif #if defined(TARGET_ARM64) && defined(TARGET_UNIX) -void (*FixupPrecode::FixupPrecodeCode)(); -void (*FixupPrecode::FixupPrecodeCode_End)(); +static void (*FixupPrecodeCode)(); +static void (*FixupPrecodeCode_End)(); #endif #ifdef FEATURE_MAP_THUNKS_FROM_IMAGE @@ -679,21 +716,43 @@ void FixupPrecode::StaticInitialize() #else _ASSERTE((SIZE_T)((BYTE*)FixupPrecodeCode_End - (BYTE*)FixupPrecodeCode) <= FixupPrecode::CodeSize); #endif -#ifdef TARGET_LOONGARCH64 - _ASSERTE(((*((short*)PCODEToPINSTR((PCODE)StubPrecodeCode) + OFFSETOF_PRECODE_TYPE)) >> 5) == StubPrecode::Type); -#elif TARGET_RISCV64 - _ASSERTE((*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE)) == FixupPrecode::Type); -#else - _ASSERTE(*((BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode) + OFFSETOF_PRECODE_TYPE) == FixupPrecode::Type); -#endif + _ASSERTE(IsFixupPrecodeByASM_DAC((PCODE)FixupPrecodeCode)); + if (FixupPrecodeCodeTemplate != NULL) + { + _ASSERTE(IsFixupPrecodeByASM_DAC((PCODE)FixupPrecodeCodeTemplate)); + } + InitializeLoaderHeapConfig(&s_fixupStubPrecodeHeapConfig, FixupPrecode::CodeSize, (void*)FixupPrecodeCodeTemplate, FixupPrecode::GenerateCodePage, FixupPrecode::GenerateDataPage); +} - InitializeLoaderHeapConfig(&s_fixupStubPrecodeHeapConfig, FixupPrecode::CodeSize, (void*)FixupPrecodeCodeTemplate, FixupPrecode::GenerateCodePage); +void FixupPrecode::GenerateDataPage(uint8_t* pageBase, size_t pageSize) +{ +#ifndef TARGET_WASM + // Fill in the data page such that the target of the fixup precode starts as initialized to point + // to the start of the precode itself, so that before the memory for the precode is initialized, + // the precode is in a state where it will loop forever. + // When initializing the precode to have the MethodDesc/Prestub target, the write to update the Target + // to go through the code which passes the extra MethodDesc argument, will be done with a VolatileStore + // such that both the MethodDesc and the PrecodeFixupThunk are updated before the Target is updated to + // make it possible to hit that code. Finally, the FixupPrecode assembly logic will have a load memory + // barrier, which will match up with that store. + // + // Finally, to make this all work, we ensure that this data page generation function is called + // BEFORE the code page is ever mapped into memory, so that it cannot be speculatively executed + // before this logic completes. + int totalCodeSize = (int)((pageSize / FixupPrecode::CodeSize) * FixupPrecode::CodeSize); + for (int i = 0; i < totalCodeSize; i += FixupPrecode::CodeSize) + { + PCODE* ppTargetSlot = (PCODE*)(pageBase + i + offsetof(FixupPrecodeData, Target)); + *ppTargetSlot = ((Precode*)(pageBase - pageSize + i))->GetEntryPoint(); + } +#endif // !TARGET_WASM } void FixupPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t pageSize) { +#ifndef TARGET_WASM + int totalCodeSize = (int)((pageSize / FixupPrecode::CodeSize) * FixupPrecode::CodeSize); #ifdef TARGET_X86 - int totalCodeSize = (pageSize / FixupPrecode::CodeSize) * FixupPrecode::CodeSize; for (int i = 0; i < totalCodeSize; i += FixupPrecode::CodeSize) { @@ -710,6 +769,14 @@ void FixupPrecode::GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size #else // TARGET_X86 FillStubCodePage(pageBase, (const void*)PCODEToPINSTR((PCODE)FixupPrecodeCode), FixupPrecode::CodeSize, pageSize); #endif // TARGET_X86 +#ifdef _DEBUG + for (int i = 0; i < totalCodeSize; i += FixupPrecode::CodeSize) + { + _ASSERTE(FixupPrecode::IsFixupPrecodeByASM((PCODE)(pageBaseRX + i))); + _ASSERTE(FixupPrecode::IsFixupPrecodeByASM_DAC((PCODE)(pageBaseRX + i))); + } +#endif // _DEBUG +#endif // !TARGET_WASM } BOOL FixupPrecode::IsFixupPrecodeByASM(PCODE addr) @@ -775,35 +842,114 @@ BOOL DoesSlotCallPrestub(PCODE pCode) void PrecodeMachineDescriptor::Init(PrecodeMachineDescriptor *dest) { - dest->OffsetOfPrecodeType = OFFSETOF_PRECODE_TYPE; - // cDAC will do (where N = 8*ReadWidthOfPrecodeType): - // uintN_t PrecodeType = *(uintN_t*)(pPrecode + OffsetOfPrecodeType); - // PrecodeType >>= ShiftOfPrecodeType; - // return (byte)PrecodeType; -#ifdef TARGET_LOONGARCH64 - dest->ReadWidthOfPrecodeType = 2; -#else - dest->ReadWidthOfPrecodeType = 1; -#endif -#if defined(SHIFTOF_PRECODE_TYPE) - dest->ShiftOfPrecodeType = SHIFTOF_PRECODE_TYPE; -#else - dest->ShiftOfPrecodeType = 0; + dest->InvalidPrecodeType = PRECODE_INVALID; +#ifdef HAS_NDIRECT_IMPORT_PRECODE + dest->PInvokeImportPrecodeType = PRECODE_NDIRECT_IMPORT; #endif - dest->InvalidPrecodeType = InvalidPrecode::Type; - dest->StubPrecodeType = StubPrecode::Type; -#ifdef HAS_NDIRECT_IMPORT_PRECODE - dest->PInvokeImportPrecodeType = NDirectImportPrecode::Type; -#endif // HAS_NDIRECT_IMPORT_PRECODE #ifdef HAS_FIXUP_PRECODE - dest->FixupPrecodeType = FixupPrecode::Type; -#endif + dest->FixupPrecodeType = PRECODE_FIXUP; + dest->FixupCodeOffset = FixupPrecode::FixupCodeOffset; + dest->FixupStubPrecodeSize = FixupPrecode::CodeSize; + + memset(dest->FixupBytes, 0, FixupPrecode::CodeSize); + memset(dest->FixupIgnoredBytes, 0, FixupPrecode::CodeSize); + + BYTE *pTemplateInstr; + BYTE *pTemplateInstrEnd; + uint8_t bytesMeaningful; +#ifdef TARGET_X86 + memset(dest->FixupIgnoredBytes + SYMBOL_VALUE(FixupPrecodeCode_Target_Offset), 1, 4); + memset(dest->FixupIgnoredBytes + SYMBOL_VALUE(FixupPrecodeCode_MethodDesc_Offset), 1, 4); + memset(dest->FixupIgnoredBytes + SYMBOL_VALUE(FixupPrecodeCode_PrecodeFixupThunk_Offset), 1, 4); +#endif // TARGET_X86 + pTemplateInstr = (BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode); + pTemplateInstrEnd = (BYTE*)PCODEToPINSTR((PCODE)FixupPrecodeCode_End); + bytesMeaningful = (uint8_t)(pTemplateInstrEnd - pTemplateInstr); + memcpy(dest->FixupBytes, pTemplateInstr, bytesMeaningful); + memset(dest->FixupIgnoredBytes + bytesMeaningful, 1, FixupPrecode::CodeSize - bytesMeaningful); +#endif // HAS_FIXUP_PRECODE + + dest->StubPrecodeSize = StubPrecode::CodeSize; + dest->StubPrecodeType = PRECODE_STUB; + + memset(dest->StubBytes, 0, StubPrecode::CodeSize); + memset(dest->StubIgnoredBytes, 0, StubPrecode::CodeSize); +#ifdef TARGET_X86 + memset(dest->StubIgnoredBytes + SYMBOL_VALUE(StubPrecodeCode_Target_Offset), 1, 4); + memset(dest->StubIgnoredBytes + SYMBOL_VALUE(StubPrecodeCode_MethodDesc_Offset), 1, 4); +#endif // TARGET_X86 + pTemplateInstr = (BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode); + pTemplateInstrEnd = (BYTE*)PCODEToPINSTR((PCODE)StubPrecodeCode_End); + bytesMeaningful = (uint8_t)(pTemplateInstrEnd - pTemplateInstr); + memcpy(dest->StubBytes, pTemplateInstr, bytesMeaningful); + memset(dest->StubIgnoredBytes + bytesMeaningful, 1, StubPrecode::CodeSize - bytesMeaningful); + #ifdef HAS_THISPTR_RETBUF_PRECODE - dest->ThisPointerRetBufPrecodeType = ThisPtrRetBufPrecode::Type; + dest->ThisPointerRetBufPrecodeType = PRECODE_THISPTR_RETBUF; +#endif + +#ifdef FEATURE_INTERPRETER + dest->InterpreterPrecodeType = PRECODE_INTERPRETER; +#endif +#ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS + dest->DynamicHelperPrecodeType = PRECODE_DYNAMIC_HELPERS; #endif + dest->UMEntryPrecodeType = PRECODE_UMENTRY_THUNK; + dest->StubCodePageSize = GetStubCodePageSize(); } #endif // !DACCESS_COMPILE +#include + +#ifdef HAS_FIXUP_PRECODE +#ifndef DACCESS_COMPILE +BOOL FixupPrecode::IsFixupPrecodeByASM_DAC(PCODE addr) +#else +BOOL FixupPrecode::IsFixupPrecodeByASM(PCODE addr) +#endif +{ + LIMITED_METHOD_DAC_CONTRACT; + PrecodeMachineDescriptor *precodeDescriptor = &(&g_cdacPlatformMetadata)->precode; + PTR_BYTE pInstr = dac_cast(PCODEToPINSTR(addr)); + for (int i = 0; i < precodeDescriptor->FixupStubPrecodeSize; i++) + { + if (precodeDescriptor->FixupIgnoredBytes[i] == 0) + { + if (precodeDescriptor->FixupBytes[i] != *pInstr) + { + return FALSE; + } + } + pInstr++; + } + + return TRUE; +} +#endif // HAS_FIXUP_PRECODE + +#ifndef DACCESS_COMPILE +BOOL StubPrecode::IsStubPrecodeByASM_DAC(PCODE addr) +#else +BOOL StubPrecode::IsStubPrecodeByASM(PCODE addr) +#endif +{ + LIMITED_METHOD_DAC_CONTRACT; + PrecodeMachineDescriptor *precodeDescriptor = &(&g_cdacPlatformMetadata)->precode; + PTR_BYTE pInstr = dac_cast(PCODEToPINSTR(addr)); + for (int i = 0; i < precodeDescriptor->StubPrecodeSize; i++) + { + if (precodeDescriptor->StubIgnoredBytes[i] == 0) + { + if (precodeDescriptor->StubBytes[i] != *pInstr) + { + return FALSE; + } + } + pInstr++; + } + + return TRUE; +} diff --git a/src/coreclr/vm/precode.h b/src/coreclr/vm/precode.h index 5c0a7299da4c93..f55407c2928cfc 100644 --- a/src/coreclr/vm/precode.h +++ b/src/coreclr/vm/precode.h @@ -13,47 +13,34 @@ #if defined(TARGET_AMD64) -#define OFFSETOF_PRECODE_TYPE 0 -#define OFFSETOF_PRECODE_TYPE_CALL_OR_JMP 5 -#define OFFSETOF_PRECODE_TYPE_MOV_R10 10 - #define SIZEOF_PRECODE_BASE 16 #elif defined(TARGET_X86) EXTERN_C VOID STDCALL PrecodeRemotingThunk(); -#define OFFSETOF_PRECODE_TYPE 0 -#define OFFSETOF_PRECODE_TYPE_CALL_OR_JMP 5 -#define OFFSETOF_PRECODE_TYPE_MOV_RM_R 6 - #define SIZEOF_PRECODE_BASE 8 #elif defined(TARGET_ARM64) #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN -#define OFFSETOF_PRECODE_TYPE 0 #elif defined(TARGET_ARM) #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN * 2 -#define OFFSETOF_PRECODE_TYPE 7 #elif defined(TARGET_LOONGARCH64) #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN -#define OFFSETOF_PRECODE_TYPE 0 #define SHIFTOF_PRECODE_TYPE 5 #elif defined(TARGET_RISCV64) #define SIZEOF_PRECODE_BASE CODE_SIZE_ALIGN -#define OFFSETOF_PRECODE_TYPE 0 #elif defined(TARGET_WASM) #define SIZEOF_PRECODE_BASE 0 -#define OFFSETOF_PRECODE_TYPE 0 #endif // TARGET_AMD64 @@ -67,19 +54,7 @@ BOOL DoesSlotCallPrestub(PCODE pCode); // Invalid precode type struct InvalidPrecode { -#if defined(TARGET_AMD64) || defined(TARGET_X86) - // int3 - static const int Type = 0xCC; -#elif defined(TARGET_ARM64) || defined(TARGET_ARM) - static const int Type = 0; -#elif defined(TARGET_LOONGARCH64) static const int Type = 0xff; -#elif defined(TARGET_RISCV64) - static const int Type = 0xff; -#elif defined(TARGET_WASM) - // unreachable instruction - static const int Type = 0; -#endif }; struct StubPrecodeData @@ -111,36 +86,25 @@ typedef DPTR(class UMEntryThunk) PTR_UMEntryThunk; // Regular precode struct StubPrecode { + static const BYTE Type = 0x3; #if defined(TARGET_AMD64) - static const BYTE Type = 0x4C; static const SIZE_T CodeSize = 24; #elif defined(TARGET_X86) - static const BYTE Type = 0xA1; static const SIZE_T CodeSize = 24; #elif defined(TARGET_ARM64) - static const int Type = 0x4A; static const SIZE_T CodeSize = 24; #elif defined(TARGET_ARM) - static const int Type = 0xFF; static const SIZE_T CodeSize = 12; #elif defined(TARGET_LOONGARCH64) - static const int Type = 0x4; static const SIZE_T CodeSize = 24; #elif defined(TARGET_RISCV64) - static const int Type = 0x17; static const SIZE_T CodeSize = 24; #elif defined(TARGET_WASM) - static const int Type = 0; static const SIZE_T CodeSize = 0; #endif // TARGET_AMD64 BYTE m_code[CodeSize]; -#if defined(TARGET_ARM64) && defined(TARGET_UNIX) - static void (*StubPrecodeCode)(); - static void (*StubPrecodeCode_End)(); -#endif - void Init(StubPrecode* pPrecodeRX, TADDR secretParam, LoaderAllocator *pLoaderAllocator = NULL, TADDR type = StubPrecode::Type, TADDR target = 0); static void StaticInitialize(); @@ -182,8 +146,9 @@ struct StubPrecode BYTE GetType(); -#ifndef DACCESS_COMPILE static BOOL IsStubPrecodeByASM(PCODE addr); + static BOOL IsStubPrecodeByASM_DAC(PCODE addr); +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { @@ -393,48 +358,38 @@ extern "C" void FixupPrecodeCode_End(); // The fixup precode is simple jump once patched. It does not have the two instruction overhead of regular precode. struct FixupPrecode { + static const int Type = 0x2; #if defined(TARGET_AMD64) - static const int Type = 0xFF; static const SIZE_T CodeSize = 24; static const int FixupCodeOffset = 6; #elif defined(TARGET_X86) - static const int Type = 0xFF; static const SIZE_T CodeSize = 24; static const int FixupCodeOffset = 6; #elif defined(TARGET_ARM64) - static const int Type = 0x0B; static const SIZE_T CodeSize = 24; static const int FixupCodeOffset = 8; #elif defined(TARGET_ARM) - static const int Type = 0xCF; - static const SIZE_T CodeSize = 12; + static const SIZE_T CodeSize = 16; static const int FixupCodeOffset = 4 + THUMB_CODE; #elif defined(TARGET_LOONGARCH64) - static const int Type = 0x3; static const SIZE_T CodeSize = 32; static const int FixupCodeOffset = 12; #elif defined(TARGET_RISCV64) - static const int Type = 0x97; static const SIZE_T CodeSize = 32; static const int FixupCodeOffset = 10; #elif defined(TARGET_WASM) - static const int Type = 2; static const SIZE_T CodeSize = 0; static const int FixupCodeOffset = 0; #endif // TARGET_AMD64 BYTE m_code[CodeSize]; -#if defined(TARGET_ARM64) && defined(TARGET_UNIX) - static void (*FixupPrecodeCode)(); - static void (*FixupPrecodeCode_End)(); -#endif - void Init(FixupPrecode* pPrecodeRX, MethodDesc* pMD, LoaderAllocator *pLoaderAllocator); static void StaticInitialize(); static void GenerateCodePage(uint8_t* pageBase, uint8_t* pageBaseRX, size_t size); + static void GenerateDataPage(uint8_t* pageBase, size_t size); PTR_FixupPrecodeData GetData() const { @@ -460,8 +415,9 @@ struct FixupPrecode return &GetData()->Target; } -#ifndef DACCESS_COMPILE static BOOL IsFixupPrecodeByASM(PCODE addr); + static BOOL IsFixupPrecodeByASM_DAC(PCODE addr); +#ifndef DACCESS_COMPILE void ResetTargetInterlocked() { @@ -517,21 +473,21 @@ typedef DPTR(FixupPrecode) PTR_FixupPrecode; typedef DPTR(class Precode) PTR_Precode; enum PrecodeType { - PRECODE_INVALID = InvalidPrecode::Type, - PRECODE_STUB = StubPrecode::Type, + PRECODE_INVALID = InvalidPrecode::Type, // 0xFF + PRECODE_STUB = StubPrecode::Type, // 0x3 #ifdef FEATURE_INTERPRETER - PRECODE_INTERPRETER = InterpreterPrecode::Type, + PRECODE_INTERPRETER = InterpreterPrecode::Type, // 0x6 #endif // FEATURE_INTERPRETER #ifdef HAS_NDIRECT_IMPORT_PRECODE - PRECODE_NDIRECT_IMPORT = NDirectImportPrecode::Type, + PRECODE_NDIRECT_IMPORT = NDirectImportPrecode::Type, // 0x5 #endif // HAS_NDIRECT_IMPORT_PRECODE #ifdef HAS_FIXUP_PRECODE PRECODE_FIXUP = FixupPrecode::Type, #endif // HAS_FIXUP_PRECODE #ifdef HAS_THISPTR_RETBUF_PRECODE - PRECODE_THISPTR_RETBUF = ThisPtrRetBufPrecode::Type, + PRECODE_THISPTR_RETBUF = ThisPtrRetBufPrecode::Type, // 0x8 #endif // HAS_THISPTR_RETBUF_PRECODE - PRECODE_UMENTRY_THUNK = PRECODE_UMENTRY_THUNK_VALUE, // Set the value here and not in UMEntryThunk to avoid circular dependency + PRECODE_UMENTRY_THUNK = PRECODE_UMENTRY_THUNK_VALUE, // 0x7 - Set the value here and not in UMEntryThunk to avoid circular dependency #ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS PRECODE_DYNAMIC_HELPERS = 0xa, #endif // FEATURE_STUBPRECODE_DYNAMIC_HELPERS @@ -661,35 +617,29 @@ class Precode { LIMITED_METHOD_CONTRACT; SUPPORTS_DAC; -#ifdef OFFSETOF_PRECODE_TYPE + PrecodeType basicPrecodeType = PRECODE_INVALID; + if (StubPrecode::IsStubPrecodeByASM(PINSTRToPCODE(dac_cast(this)))) + { + basicPrecodeType = PRECODE_STUB; + } -#if defined(TARGET_LOONGARCH64) - assert(0 == OFFSETOF_PRECODE_TYPE); - static_assert(5 == SHIFTOF_PRECODE_TYPE, "expected shift of 5"); - short type = *((short*)m_data); - type >>= SHIFTOF_PRECODE_TYPE; -#elif defined(TARGET_RISCV64) - assert(0 == OFFSETOF_PRECODE_TYPE); - BYTE type = *((BYTE*)m_data + OFFSETOF_PRECODE_TYPE); -#else -#if defined(SHIFTOF_PRECODE_TYPE) -#error "did not expect SHIFTOF_PRECODE_TYPE to be defined" -#endif - BYTE type = m_data[OFFSETOF_PRECODE_TYPE]; +#ifdef HAS_FIXUP_PRECODE + if (FixupPrecode::IsFixupPrecodeByASM(PINSTRToPCODE(dac_cast(this)))) + { + basicPrecodeType = PRECODE_FIXUP; + } #endif - if (type == StubPrecode::Type) + if (basicPrecodeType == PRECODE_STUB) { // StubPrecode code is used for both StubPrecode, NDirectImportPrecode, InterpreterPrecode, and ThisPtrRetBufPrecode, // so we need to get the real type - type = AsStubPrecode()->GetType(); + return (PrecodeType)AsStubPrecode()->GetType(); + } + else + { + return basicPrecodeType; } - - return (PrecodeType)type; - -#else // OFFSETOF_PRECODE_TYPE - return PRECODE_STUB; -#endif // OFFSETOF_PRECODE_TYPE } static BOOL IsValidType(PrecodeType t); @@ -788,10 +738,20 @@ class Precode { // Always do consistency check in debug if (fSpeculative INDEBUG(|| TRUE)) { - if (!IS_ALIGNED(pInstr, PRECODE_ALIGNMENT) || !IsValidType(PTR_Precode(pInstr)->GetType())) + if (!IS_ALIGNED(pInstr, PRECODE_ALIGNMENT)) + { + // This not a fixup precode or stub precode + return NULL; + } + if (!StubPrecode::IsStubPrecodeByASM(addr)) { - if (fSpeculative) return NULL; - _ASSERTE(!"Precode::GetPrecodeFromEntryPoint: Unexpected code in precode"); +#ifdef HAS_FIXUP_PRECODE + if (!FixupPrecode::IsFixupPrecodeByASM(addr)) +#endif + { + // This not a fixup precode or stub precode + return NULL; + } } } @@ -831,41 +791,47 @@ static_assert_no_msg(sizeof(Precode) <= sizeof(NDirectImportPrecode)); static_assert_no_msg(sizeof(Precode) <= sizeof(FixupPrecode)); static_assert_no_msg(sizeof(Precode) <= sizeof(ThisPtrRetBufPrecode)); -#ifndef DACCESS_COMPILE // A summary of the precode layout for diagnostic purposes struct PrecodeMachineDescriptor { uint32_t StubCodePageSize; - - uint8_t OffsetOfPrecodeType; - // cDAC will do (where N = 8*ReadWidthOfPrecodeType): - // uintN_t PrecodeType = *(uintN_t*)(pPrecode + OffsetOfPrecodeType); - // PrecodeType >>= ShiftOfPrecodeType; - // return (byte)PrecodeType; - uint8_t ReadWidthOfPrecodeType; - uint8_t ShiftOfPrecodeType; - uint8_t InvalidPrecodeType; - uint8_t StubPrecodeType; #ifdef HAS_NDIRECT_IMPORT_PRECODE uint8_t PInvokeImportPrecodeType; #endif #ifdef HAS_FIXUP_PRECODE uint8_t FixupPrecodeType; -#endif + uint8_t FixupCodeOffset; + uint8_t FixupStubPrecodeSize; + BYTE FixupBytes[FixupPrecode::CodeSize]; + BYTE FixupIgnoredBytes[FixupPrecode::CodeSize]; +#endif // HAS_FIXUP_PRECODE + + uint8_t StubPrecodeSize; + uint8_t StubPrecodeType; + + BYTE StubBytes[StubPrecode::CodeSize]; + BYTE StubIgnoredBytes[StubPrecode::CodeSize]; #ifdef HAS_THISPTR_RETBUF_PRECODE uint8_t ThisPointerRetBufPrecodeType; #endif +#ifdef FEATURE_INTERPRETER + uint8_t InterpreterPrecodeType; +#endif +#ifdef FEATURE_STUBPRECODE_DYNAMIC_HELPERS + uint8_t DynamicHelperPrecodeType; +#endif + uint8_t UMEntryPrecodeType; + public: PrecodeMachineDescriptor() = default; PrecodeMachineDescriptor(const PrecodeMachineDescriptor&) = delete; PrecodeMachineDescriptor& operator=(const PrecodeMachineDescriptor&) = delete; static void Init(PrecodeMachineDescriptor* dest); }; -#endif //DACCESS_COMPILE extern InterleavedLoaderHeapConfig s_stubPrecodeHeapConfig; #ifdef HAS_FIXUP_PRECODE diff --git a/src/coreclr/vm/prestub.cpp b/src/coreclr/vm/prestub.cpp index d63251294dab6a..af26f04428ee27 100644 --- a/src/coreclr/vm/prestub.cpp +++ b/src/coreclr/vm/prestub.cpp @@ -3300,7 +3300,7 @@ PCODE DynamicHelperFixup(TransitionBlock * pTransitionBlock, TADDR * pCell, DWOR if (pHelper != (PCODE)NULL) { - *(TADDR *)pCell = pHelper; + VolatileStore((TADDR *)pCell, pHelper); } #ifdef _DEBUG diff --git a/src/coreclr/vm/readytoruninfo.cpp b/src/coreclr/vm/readytoruninfo.cpp index 2fa9632fea82b8..0910f94ba47f33 100644 --- a/src/coreclr/vm/readytoruninfo.cpp +++ b/src/coreclr/vm/readytoruninfo.cpp @@ -2033,9 +2033,18 @@ PCODE CreateDynamicHelperPrecode(LoaderAllocator *pAllocator, AllocMemTracker *p STANDARD_VM_CONTRACT; size_t size = sizeof(StubPrecode); - StubPrecode *pPrecode = (StubPrecode *)pamTracker->Track(pAllocator->GetDynamicHelpersStubHeap()->AllocAlignedMem(size, 1)); + StubPrecode *pPrecode = (StubPrecode *)pamTracker->Track(pAllocator->GetDynamicHelpersStubHeap()->AllocStub()); pPrecode->Init(pPrecode, DynamicHelperArg, pAllocator, PRECODE_DYNAMIC_HELPERS, DynamicHelper); +#ifdef FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + // While the allocation of a precode will use a memory mapping technique, and not actually write to the set of instructions, + // the set of instructions in the stub has non-barrier protected reads from the StubPrecodeData structure. In order to protect those + // reads we would either need barrier instructions in the stub, or we need to ensure that the precode is flushed in the instruction cache + // which will have the side effect of ensuring that the reads within the stub will happen *after* the writes to the StubPrecodeData structure which + // happened in the Init routine above. + ClrFlushInstructionCache(pPrecode, sizeof(StubPrecode)); +#endif // FEATURE_CORECLR_FLUSH_INSTRUCTION_CACHE_TO_PROTECT_STUB_READS + #ifdef FEATURE_PERFMAP PerfMap::LogStubs(__FUNCTION__, "DynamicHelper", (PCODE)pPrecode, size, PerfMapStubType::IndividualWithinBlock); #endif diff --git a/src/coreclr/vm/riscv64/thunktemplates.S b/src/coreclr/vm/riscv64/thunktemplates.S index 8b58b4ec48b0db..e2990d44c57eee 100644 --- a/src/coreclr/vm/riscv64/thunktemplates.S +++ b/src/coreclr/vm/riscv64/thunktemplates.S @@ -16,9 +16,10 @@ LEAF_ENTRY FixupPrecodeCode ld t2, (FixupPrecodeData__Target)(t2) c.jr t2 + fence r,rw auipc t2, 0x4 - ld t1, (FixupPrecodeData__PrecodeFixupThunk - 0xa)(t2) - ld t2, (FixupPrecodeData__MethodDesc - 0xa)(t2) + ld t1, (FixupPrecodeData__PrecodeFixupThunk - 0xe)(t2) + ld t2, (FixupPrecodeData__MethodDesc - 0xe)(t2) jr t1 LEAF_END_MARKED FixupPrecodeCode diff --git a/src/libraries/externals.csproj b/src/libraries/externals.csproj index 480419db34f86f..e207c88bf99da1 100644 --- a/src/libraries/externals.csproj +++ b/src/libraries/externals.csproj @@ -90,8 +90,8 @@ - - + + diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubsFactory.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubsFactory.cs index 2687e1d361fe96..c47d671ed5058d 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubsFactory.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubsFactory.cs @@ -17,6 +17,7 @@ IPrecodeStubs IContractFactory.CreateContract(Target target, int { 1 => new PrecodeStubs_1(target, precodeMachineDescriptor, codePointerFlags), 2 => new PrecodeStubs_2(target, precodeMachineDescriptor, codePointerFlags), + 3 => new PrecodeStubs_3(target, precodeMachineDescriptor, codePointerFlags), _ => default(PrecodeStubs), }; } diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_1.cs index 1fec6033ad9e9f..d34699a6314f7e 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_1.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using Microsoft.Diagnostics.DataContractReader.Data; namespace Microsoft.Diagnostics.DataContractReader.Contracts; @@ -31,6 +32,77 @@ public static byte StubPrecodeData_GetType(Data.StubPrecodeData_1 stubPrecodeDat { return stubPrecodeData.Type; } + + internal static byte ReadPrecodeType(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + if (precodeMachineDescriptor.ReadWidthOfPrecodeType!.Value == 1) + { + byte precodeType = target.Read(instrPointer + precodeMachineDescriptor.OffsetOfPrecodeType!.Value); + return (byte)(precodeType >> precodeMachineDescriptor.ShiftOfPrecodeType!.Value); + } + else if (precodeMachineDescriptor.ReadWidthOfPrecodeType!.Value == 2) + { + ushort precodeType = target.Read(instrPointer + precodeMachineDescriptor.OffsetOfPrecodeType!.Value); + return (byte)(precodeType >> precodeMachineDescriptor.ShiftOfPrecodeType!.Value); + } + else + { + throw new InvalidOperationException($"Invalid precode type width {precodeMachineDescriptor.ReadWidthOfPrecodeType}"); + } + } + + public static KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + return TryGetKnownPrecodeType_Impl(instrPointer, target, precodeMachineDescriptor); + } + + public static KnownPrecodeType? TryGetKnownPrecodeType_Impl(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) where TPrecodeStubsImplementation : IPrecodeStubsContractCommonApi where TStubPrecodeData : IData + { + // We get the precode type in two phases: + // 1. Read the precode type from the intruction address. + // 2. If it's "stub", look at the stub data and get the actual precode type - it could be stub, + // but it could also be a pinvoke precode or a ThisPtrRetBufPrecode + // precode.h Precode::GetType() + byte approxPrecodeType = ReadPrecodeType(instrPointer, target, precodeMachineDescriptor); + byte exactPrecodeType; + if (approxPrecodeType == precodeMachineDescriptor.StubPrecodeType) + { + // get the actual type from the StubPrecodeData + TStubPrecodeData stubPrecodeData = GetStubPrecodeData(instrPointer, target, precodeMachineDescriptor); + exactPrecodeType = TPrecodeStubsImplementation.StubPrecodeData_GetType(stubPrecodeData); + } + else + { + exactPrecodeType = approxPrecodeType; + } + + if (exactPrecodeType == precodeMachineDescriptor.StubPrecodeType) + { + return KnownPrecodeType.Stub; + } + else if (precodeMachineDescriptor.PInvokeImportPrecodeType is byte ndType && exactPrecodeType == ndType) + { + return KnownPrecodeType.PInvokeImport; + } + else if (precodeMachineDescriptor.FixupPrecodeType is byte fixupType && exactPrecodeType == fixupType) + { + return KnownPrecodeType.Fixup; + } + else if (precodeMachineDescriptor.ThisPointerRetBufPrecodeType is byte thisPtrRetBufType && exactPrecodeType == thisPtrRetBufType) + { + return KnownPrecodeType.ThisPtrRetBuf; + } + else + { + return null; + } + + static TStubPrecodeData GetStubPrecodeData(TargetPointer stubInstrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + TargetPointer stubPrecodeDataAddress = stubInstrPointer + precodeMachineDescriptor.StubCodePageSize; + return target.ProcessedData.GetOrAdd(stubPrecodeDataAddress); + } + } } internal sealed class PrecodeStubs_1 : PrecodeStubsCommon diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_2.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_2.cs index 37d73a2df7b288..f62cb97b29c255 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_2.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_2.cs @@ -23,16 +23,22 @@ public static TargetPointer FixupPrecode_GetMethodDesc(TargetPointer instrPointe public static TargetPointer ThisPtrRetBufPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) { - TargetPointer stubPrecodeDataAddress = instrPointer + precodeMachineDescriptor.StubCodePageSize; - Data.StubPrecodeData_2 stubPrecodeData = target.ProcessedData.GetOrAdd(stubPrecodeDataAddress); - Data.ThisPtrRetBufPrecodeData thisPtrRetBufPrecodeData = target.ProcessedData.GetOrAdd(stubPrecodeData.SecretParam); - return thisPtrRetBufPrecodeData.MethodDesc; + TargetPointer stubPrecodeDataAddress = instrPointer + precodeMachineDescriptor.StubCodePageSize; + Data.StubPrecodeData_2 stubPrecodeData = target.ProcessedData.GetOrAdd(stubPrecodeDataAddress); + Data.ThisPtrRetBufPrecodeData thisPtrRetBufPrecodeData = target.ProcessedData.GetOrAdd(stubPrecodeData.SecretParam); + return thisPtrRetBufPrecodeData.MethodDesc; } public static byte StubPrecodeData_GetType(Data.StubPrecodeData_2 stubPrecodeData) { return stubPrecodeData.Type; } + + public static KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + // Version 2 of this contract behaves just like version 1 other than the details that are abstracted away through the IPrecodeStubsContractCommonApi interface + return PrecodeStubs_1_Impl.TryGetKnownPrecodeType_Impl(instrPointer, target, precodeMachineDescriptor); + } } internal sealed class PrecodeStubs_2 : PrecodeStubsCommon diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_3.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_3.cs new file mode 100644 index 00000000000000..d9c52c907defee --- /dev/null +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_3.cs @@ -0,0 +1,104 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; + +namespace Microsoft.Diagnostics.DataContractReader.Contracts; + +internal struct PrecodeStubs_3_Impl : IPrecodeStubsContractCommonApi +{ + public static TargetPointer StubPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + // Version 3 of this contract behaves just like version 2 + return PrecodeStubs_2_Impl.StubPrecode_GetMethodDesc(instrPointer, target, precodeMachineDescriptor); + } + + public static TargetPointer FixupPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + // Version 3 of this contract behaves just like version 1 + return PrecodeStubs_1_Impl.FixupPrecode_GetMethodDesc(instrPointer, target, precodeMachineDescriptor); + } + + public static TargetPointer ThisPtrRetBufPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + // Version 3 of this contract behaves just like version 2 + return PrecodeStubs_2_Impl.ThisPtrRetBufPrecode_GetMethodDesc(instrPointer, target, precodeMachineDescriptor); + } + + public static byte StubPrecodeData_GetType(Data.StubPrecodeData_2 stubPrecodeData) + { + // Version 3 of this contract behaves just like version 2 + return PrecodeStubs_2_Impl.StubPrecodeData_GetType(stubPrecodeData); + } + + private static Data.StubPrecodeData_2 GetStubPrecodeData(TargetPointer stubInstrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + TargetPointer stubPrecodeDataAddress = stubInstrPointer + precodeMachineDescriptor.StubCodePageSize; + return target.ProcessedData.GetOrAdd(stubPrecodeDataAddress); + } + + public static KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor) + { + if (ReadBytesAndCompare(instrPointer, precodeMachineDescriptor.StubBytes!, precodeMachineDescriptor.StubIgnoredBytes!, target)) + { + // get the actual type from the StubPrecodeData + Data.StubPrecodeData_2 stubPrecodeData = GetStubPrecodeData(instrPointer, target, precodeMachineDescriptor); + byte exactPrecodeType = stubPrecodeData.Type; + if (exactPrecodeType == 0) + return null; + + if (exactPrecodeType == precodeMachineDescriptor.StubPrecodeType) + { + return KnownPrecodeType.Stub; + } + else if (precodeMachineDescriptor.PInvokeImportPrecodeType is byte compareByte1 && compareByte1 == exactPrecodeType) + { + return KnownPrecodeType.PInvokeImport; + } + else if (precodeMachineDescriptor.ThisPointerRetBufPrecodeType is byte compareByte2 && compareByte2 == exactPrecodeType) + { + return KnownPrecodeType.ThisPtrRetBuf; + } + else if (precodeMachineDescriptor.UMEntryPrecodeType is byte compareByte3 && compareByte3 == exactPrecodeType) + { + return KnownPrecodeType.UMEntry; + } + else if (precodeMachineDescriptor.InterpreterPrecodeType is byte compareByte4 && compareByte4 == exactPrecodeType) + { + return KnownPrecodeType.Interpreter; + } + else if (precodeMachineDescriptor.DynamicHelperPrecodeType is byte compareByte5 && compareByte5 == exactPrecodeType) + { + return KnownPrecodeType.DynamicHelper; + } + } + else if (ReadBytesAndCompare(instrPointer, precodeMachineDescriptor.FixupBytes!, precodeMachineDescriptor.FixupIgnoredBytes!, target)) + { + return KnownPrecodeType.Fixup; + } + return null; + + static bool ReadBytesAndCompare(TargetPointer instrAddress, byte[] expectedBytePattern, byte[] bytesToIgnore, Target target) + { + for (ulong i = 0; i < (ulong)expectedBytePattern.Length; i++) + { + if (bytesToIgnore[i] == 0) + { + byte targetBytePattern = target.Read(new TargetPointer((instrAddress.Value + (ulong)i))); + if (expectedBytePattern[i] != targetBytePattern) + { + return false; + } + } + } + + return true; + } + } +} + +internal sealed class PrecodeStubs_3 : PrecodeStubsCommon +{ + public PrecodeStubs_3(Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor, CodePointerFlags codePointerFlags) : base(target, precodeMachineDescriptor, codePointerFlags) { } +} diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs index aeb97ab7a522cd..9e36351239b6bc 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/PrecodeStubs_Common.cs @@ -7,6 +7,17 @@ namespace Microsoft.Diagnostics.DataContractReader.Contracts; +internal enum KnownPrecodeType +{ + Stub = 1, + PInvokeImport, // also known as NDirectImport in the runtime + Fixup, + ThisPtrRetBuf, + UMEntry, + Interpreter, + DynamicHelper +} + // Interface used to abstract behavior which may be different between multiple versions of the precode stub implementations internal interface IPrecodeStubsContractCommonApi { @@ -14,6 +25,7 @@ internal interface IPrecodeStubsContractCommonApi public static abstract TargetPointer ThisPtrRetBufPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor); public static abstract TargetPointer FixupPrecode_GetMethodDesc(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor); public static abstract byte StubPrecodeData_GetType(TStubPrecodeData stubPrecodeData); + public static abstract KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrPointer, Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor); } internal class PrecodeStubsCommon : IPrecodeStubs where TPrecodeStubsImplementation : IPrecodeStubsContractCommonApi where TStubPrecodeData : IData @@ -22,14 +34,6 @@ internal class PrecodeStubsCommon private readonly CodePointerFlags _codePointerFlags; internal readonly Data.PrecodeMachineDescriptor MachineDescriptor; - internal enum KnownPrecodeType - { - Stub = 1, - PInvokeImport, // also known as NDirectImport in the runtime - Fixup, - ThisPtrRetBuf, - } - internal abstract class ValidPrecode { public TargetPointer InstrPointer { get; } @@ -42,7 +46,6 @@ protected ValidPrecode(TargetPointer instrPointer, KnownPrecodeType precodeType) } internal abstract TargetPointer GetMethodDesc(Target target, Data.PrecodeMachineDescriptor precodeMachineDescriptor); - } internal class StubPrecode : ValidPrecode @@ -81,24 +84,6 @@ internal override TargetPointer GetMethodDesc(Target target, Data.PrecodeMachine private bool IsAlignedInstrPointer(TargetPointer instrPointer) => _target.IsAlignedToPointerSize(instrPointer); - private byte ReadPrecodeType(TargetPointer instrPointer) - { - if (MachineDescriptor.ReadWidthOfPrecodeType == 1) - { - byte precodeType = _target.Read(instrPointer + MachineDescriptor.OffsetOfPrecodeType); - return (byte)(precodeType >> MachineDescriptor.ShiftOfPrecodeType); - } - else if (MachineDescriptor.ReadWidthOfPrecodeType == 2) - { - ushort precodeType = _target.Read(instrPointer + MachineDescriptor.OffsetOfPrecodeType); - return (byte)(precodeType >> MachineDescriptor.ShiftOfPrecodeType); - } - else - { - throw new InvalidOperationException($"Invalid precode type width {MachineDescriptor.ReadWidthOfPrecodeType}"); - } - } - private TStubPrecodeData GetStubPrecodeData(TargetPointer stubInstrPointer) { TargetPointer stubPrecodeDataAddress = stubInstrPointer + MachineDescriptor.StubCodePageSize; @@ -107,44 +92,7 @@ private TStubPrecodeData GetStubPrecodeData(TargetPointer stubInstrPointer) private KnownPrecodeType? TryGetKnownPrecodeType(TargetPointer instrAddress) { - // We get the precode type in two phases: - // 1. Read the precode type from the intruction address. - // 2. If it's "stub", look at the stub data and get the actual precode type - it could be stub, - // but it could also be a pinvoke precode or a ThisPtrRetBufPrecode - // precode.h Precode::GetType() - byte approxPrecodeType = ReadPrecodeType(instrAddress); - byte exactPrecodeType; - if (approxPrecodeType == MachineDescriptor.StubPrecodeType) - { - // get the actual type from the StubPrecodeData - TStubPrecodeData stubPrecodeData = GetStubPrecodeData(instrAddress); - exactPrecodeType = TPrecodeStubsImplementation.StubPrecodeData_GetType(stubPrecodeData); - } - else - { - exactPrecodeType = approxPrecodeType; - } - - if (exactPrecodeType == MachineDescriptor.StubPrecodeType) - { - return KnownPrecodeType.Stub; - } - else if (MachineDescriptor.PInvokeImportPrecodeType is byte ndType && exactPrecodeType == ndType) - { - return KnownPrecodeType.PInvokeImport; - } - else if (MachineDescriptor.FixupPrecodeType is byte fixupType && exactPrecodeType == fixupType) - { - return KnownPrecodeType.Fixup; - } - else if (MachineDescriptor.ThisPointerRetBufPrecodeType is byte thisPtrRetBufType && exactPrecodeType == thisPtrRetBufType) - { - return KnownPrecodeType.ThisPtrRetBuf; - } - else - { - return null; - } + return TPrecodeStubsImplementation.TryGetKnownPrecodeType(instrAddress, _target, MachineDescriptor); } internal TargetPointer CodePointerReadableInstrPointer(TargetCodePointer codePointer) diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs index d5a67fb65e411e..2459fa3225e6c4 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64/AMD64Unwinder.cs @@ -303,15 +303,17 @@ public bool Unwind(ref AMD64Context context) branchTarget = nextByte - imageBase; if (ReadByteAt(nextByte) == JMP_IMM8_OP) { - branchTarget += 2u + ReadByteAt(nextByte + 1); + // sign-extend the 8-bit immediate value + branchTarget += 2u + (ulong)(sbyte)ReadByteAt(nextByte + 1); } else { + // sign-extend the 32-bit immediate value int delta = ReadByteAt(nextByte + 1) | (ReadByteAt(nextByte + 2) << 8) | (ReadByteAt(nextByte + 3) << 16) | (ReadByteAt(nextByte + 4) << 24); - branchTarget += (uint)(5 + delta); + branchTarget += (ulong)(5 + delta); } // diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs index 86b2fa6f4ab301..a4be57139660a6 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/AMD64Context.cs @@ -31,7 +31,7 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x4d0; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public TargetPointer StackPointer { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs index 3d0fe1c2cc69b6..423c91415c4f9a 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARM64Context.cs @@ -38,7 +38,10 @@ public enum ContextFlagsValues : uint public readonly uint Size => 0x390; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + public readonly uint DefaultContextFlags => (uint)(ContextFlagsValues.CONTEXT_CONTROL | + ContextFlagsValues.CONTEXT_INTEGER | + ContextFlagsValues.CONTEXT_FLOATING_POINT | + ContextFlagsValues.CONTEXT_DEBUG_REGISTERS); public TargetPointer StackPointer { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs index af85910ff53087..c8aeb154a0e373 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/ARMContext.cs @@ -29,7 +29,7 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x1a0; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public TargetPointer StackPointer { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs index 37d3e92a4f19ee..f1024bbc23cadf 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/Context/X86Context.cs @@ -22,8 +22,9 @@ public enum ContextFlagsValues : uint CONTEXT_SEGMENTS = CONTEXT_i386 | 0x4, CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x8, CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, + CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT, - CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS, + CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS, CONTEXT_XSTATE = CONTEXT_i386 | 0x40, // @@ -37,7 +38,7 @@ public enum ContextFlagsValues : uint } public readonly uint Size => 0x2cc; - public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_FULL; + public readonly uint DefaultContextFlags => (uint)ContextFlagsValues.CONTEXT_ALL; public TargetPointer StackPointer { diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs index 2b1c42c1eae825..8e6b46a509a119 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Contracts/StackWalk/StackWalk_1.cs @@ -191,6 +191,11 @@ private unsafe void FillContextFromThread(IPlatformAgnosticContext context, Thre { byte[] bytes = new byte[context.Size]; Span buffer = new Span(bytes); + // The underlying ICLRDataTarget.GetThreadContext has some variance depending on the host. + // SOS's managed implementation sets the ContextFlags to platform specific values defined in ThreadService.cs (diagnostics repo) + // SOS's native implementation keeps the ContextFlags passed into this function. + // To match the DAC behavior, the DefaultContextFlags are what the DAC passes in in DacGetThreadContext. + // In most implementations, this will be overridden by the host, but in some cases, it may not be. if (!_target.TryGetThreadContext(threadData.OSId.Value, context.DefaultContextFlags, buffer)) { throw new InvalidOperationException($"GetThreadContext failed for thread {threadData.OSId.Value}"); diff --git a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PrecodeMachineDescriptor.cs b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PrecodeMachineDescriptor.cs index ebecc223955057..3e1ec015b41048 100644 --- a/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PrecodeMachineDescriptor.cs +++ b/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts/Data/PrecodeMachineDescriptor.cs @@ -11,46 +11,93 @@ static PrecodeMachineDescriptor IData.Create(Target ta public PrecodeMachineDescriptor(Target target, TargetPointer address) { Target.TypeInfo type = target.GetTypeInfo(DataType.PrecodeMachineDescriptor); - OffsetOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(OffsetOfPrecodeType)].Offset); - ReadWidthOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(ReadWidthOfPrecodeType)].Offset); - ShiftOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(ShiftOfPrecodeType)].Offset); - InvalidPrecodeType = target.Read(address + (ulong)type.Fields[nameof(InvalidPrecodeType)].Offset); - StubPrecodeType = target.Read(address + (ulong)type.Fields[nameof(StubPrecodeType)].Offset); - if (type.Fields.ContainsKey(nameof(PInvokeImportPrecodeType))) + if (type.Fields.ContainsKey(nameof(OffsetOfPrecodeType))) { - PInvokeImportPrecodeType = target.Read(address + (ulong)type.Fields[nameof(PInvokeImportPrecodeType)].Offset); + OffsetOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(OffsetOfPrecodeType)].Offset); + ReadWidthOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(ReadWidthOfPrecodeType)].Offset); + ShiftOfPrecodeType = target.Read(address + (ulong)type.Fields[nameof(ShiftOfPrecodeType)].Offset); } else { - PInvokeImportPrecodeType = null; + OffsetOfPrecodeType = null; + ReadWidthOfPrecodeType = null; + ShiftOfPrecodeType = null; } - if (type.Fields.ContainsKey(nameof(FixupPrecodeType))) + InvalidPrecodeType = target.Read(address + (ulong)type.Fields[nameof(InvalidPrecodeType)].Offset); + StubPrecodeType = target.Read(address + (ulong)type.Fields[nameof(StubPrecodeType)].Offset); + + if (type.Fields.ContainsKey(nameof(FixupStubPrecodeSize))) { - FixupPrecodeType = target.Read(address + (ulong)type.Fields[nameof(FixupPrecodeType)].Offset); + FixupStubPrecodeSize = target.Read(address + (ulong)type.Fields[nameof(FixupStubPrecodeSize)].Offset); + FixupBytes = new byte[FixupStubPrecodeSize.Value]; + target.ReadBuffer(address + (ulong)type.Fields[nameof(FixupBytes)].Offset, FixupBytes); + FixupIgnoredBytes = new byte[FixupStubPrecodeSize.Value]; + target.ReadBuffer(address + (ulong)type.Fields[nameof(FixupIgnoredBytes)].Offset, FixupIgnoredBytes); } else { - FixupPrecodeType = null; + FixupStubPrecodeSize = null; + FixupBytes = null; + FixupIgnoredBytes = null; } - if (type.Fields.ContainsKey(nameof(ThisPointerRetBufPrecodeType))) + + if (type.Fields.ContainsKey(nameof(StubPrecodeSize))) { - ThisPointerRetBufPrecodeType = target.Read(address + (ulong)type.Fields[nameof(ThisPointerRetBufPrecodeType)].Offset); + StubPrecodeSize = target.Read(address + (ulong)type.Fields[nameof(FixupStubPrecodeSize)].Offset); + StubBytes = new byte[StubPrecodeSize.Value]; + target.ReadBuffer(address + (ulong)type.Fields[nameof(StubBytes)].Offset, StubBytes); + StubIgnoredBytes = new byte[StubPrecodeSize.Value]; + target.ReadBuffer(address + (ulong)type.Fields[nameof(StubIgnoredBytes)].Offset, StubIgnoredBytes); } else { - ThisPointerRetBufPrecodeType = null; + StubPrecodeSize = null; + FixupBytes = null; + FixupIgnoredBytes = null; } + + PInvokeImportPrecodeType = MaybeGetPrecodeType(target, address, nameof(PInvokeImportPrecodeType)); + FixupPrecodeType = MaybeGetPrecodeType(target, address, nameof(FixupPrecodeType)); + ThisPointerRetBufPrecodeType = MaybeGetPrecodeType(target, address, nameof(ThisPointerRetBufPrecodeType)); + InterpreterPrecodeType = MaybeGetPrecodeType(target, address, nameof(InterpreterPrecodeType)); + UMEntryPrecodeType = MaybeGetPrecodeType(target, address, nameof(UMEntryPrecodeType)); + DynamicHelperPrecodeType = MaybeGetPrecodeType(target, address, nameof(DynamicHelperPrecodeType)); + StubCodePageSize = target.Read(address + (ulong)type.Fields[nameof(StubCodePageSize)].Offset); + + static byte? MaybeGetPrecodeType(Target target, TargetPointer address, string fieldName) + { + if (target.GetTypeInfo(DataType.PrecodeMachineDescriptor).Fields.ContainsKey(fieldName)) + { + return target.Read(address + (ulong)target.GetTypeInfo(DataType.PrecodeMachineDescriptor).Fields[fieldName].Offset); + } + else + { + return null; + } + } } - public byte OffsetOfPrecodeType { get; init; } - public byte ReadWidthOfPrecodeType { get; init; } - public byte ShiftOfPrecodeType { get; init; } + public byte? OffsetOfPrecodeType { get; init; } // Not present for version 3 and above + public byte? ReadWidthOfPrecodeType { get; init; } // Not present for version 3 and above + public byte? ShiftOfPrecodeType { get; init; } // Not present for version 3 and above public byte InvalidPrecodeType { get; init; } public byte StubPrecodeType { get; init; } public byte? PInvokeImportPrecodeType { get; init; } public byte? FixupPrecodeType { get; init; } public byte? ThisPointerRetBufPrecodeType { get; init; } + public byte? FixupStubPrecodeSize { get; init; } // Present for version 3 and above + public byte[]? FixupBytes { get; init; } // Present for version 3 and above + public byte[]? FixupIgnoredBytes { get; init; } // Present for version 3 and above + + public byte? StubPrecodeSize { get; init; } // Present for version 3 and above + public byte[]? StubBytes { get; init; } // Present for version 3 and above + public byte[]? StubIgnoredBytes { get; init; } // Present for version 3 and above + + public byte? InterpreterPrecodeType { get; init; } // May be present for version 3 and above + public byte? UMEntryPrecodeType { get; init; } // May be present for version 3 and above + public byte? DynamicHelperPrecodeType { get; init; } // May be present for version 3 and above + public uint StubCodePageSize { get; init; } }