Change VMPTR IPC events to ensure endianness consistency#128058
Conversation
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
There was a problem hiding this comment.
Pull request overview
This PR adjusts how VMPTR values are represented across the debugger IPC boundary to preserve endianness correctness: VMPTR becomes a host-endian wrapper over CORDB_ADDRESS, and IPC payloads should carry Portable<VMPTR> so they remain little-endian “on the wire” while converting back to host endianness on access.
Changes:
- Adds convenience forwarders on
Portable<T>to call pointer-like helpers (intended forPortable<VMPTR_*>) through the endian-converting operators. - Changes
VMPTR_Basestorage fromPortable<CORDB_ADDRESS>toCORDB_ADDRESS, and updates many IPC event fields fromVMPTR_*toPortable<VMPTR_*>. - Updates a couple of event-producing sites to initialize null
VMPTRvalues viaNullPtr()assignment.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/inc/dbgportable.h | Adds Portable<T> method forwarders intended to enable ergonomic use of Portable<VMPTR_*> fields. |
| src/coreclr/debug/inc/dbgipcevents.h | Moves endianness handling for VMPTR from inside VMPTR_Base to the IPC layer by wrapping many IPC fields in Portable<>, and adjusts some pointer field representations. |
| src/coreclr/debug/ee/debugger.cpp | Updates null initialization for vmAssembly in DebuggerIPCE_BasicTypeData to align with the new Portable<VMPTR_*> field type. |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 3
| template <typename Dummy = T> | ||
| auto GetRawPtr() -> decltype(Dummy().GetRawPtr()) | ||
| { | ||
| Dummy tmp = *this; | ||
| return tmp.GetRawPtr(); | ||
| } | ||
|
|
||
| template <typename Dummy = T> | ||
| auto GetDacPtr() -> decltype(Dummy().GetDacPtr()) | ||
| { | ||
| Dummy tmp = *this; | ||
| return tmp.GetDacPtr(); | ||
| } | ||
|
|
||
| template <typename TAddr> | ||
| void SetDacTargetPtr(TAddr addr) | ||
| { | ||
| T tmp; | ||
| tmp.SetDacTargetPtr(addr); | ||
| *this = tmp; | ||
| } | ||
|
|
||
| bool IsNull() | ||
| { | ||
| T tmp = *this; | ||
| return tmp.IsNull(); | ||
| } |
| bool IsNull() | ||
| { | ||
| T tmp = *this; |
| auto GetRawPtr() -> decltype(Dummy().GetRawPtr()) | ||
| { | ||
| Dummy tmp = *this; | ||
| return tmp.GetRawPtr(); | ||
| } | ||
|
|
||
| template <typename Dummy = T> | ||
| auto GetDacPtr() -> decltype(Dummy().GetDacPtr()) |
| bool IsNull() | ||
| { | ||
| T tmp = *this; |
| { | ||
| Portable<mdTypeDef> metadataToken; | ||
| VMPTR_Assembly vmAssembly; | ||
| VMPTR_TypeHandle typeHandle; // if non-null then further fetches will be needed to get type arguments | ||
| Portable<VMPTR_Assembly> vmAssembly; | ||
| Portable<VMPTR_TypeHandle> typeHandle; // if non-null then further fetches will be needed to get type arguments | ||
| } ClassTypeData; |
| LPVOID m_sp; | ||
| Portable<CORDB_ADDRESS> m_sp; | ||
| }; | ||
|
|
| FramePointer &operator=(const BYTE* sp); | ||
|
|
||
| LPVOID m_sp; | ||
| Portable<CORDB_ADDRESS> m_sp; |
There was a problem hiding this comment.
What if we left FramePointer as-is and just used Portable<CORDB_ADDRESS> in the small number of places where FramePointer currently shows up in the IPC definition? I'm trying to avoid embedding endian-swapping inside more types that are often used outside IPC events.
The inadvertent consequence of #127943 is that since VMPTR now wraps a
Portable<CORDB_ADDRESS>, VMPTR is propagated as a little-endian value across DBI, DAC, and EE, as opposed to solely in the IPC layer. Changing this such that VMPTR wraps CORDB_ADDRESS and we havePortable<VMPTR>in the IPC layer - remains little-endian in IPC, but the conversion operator to a VMPTR converts it to the machine endianness.