Skip to content

Commit 1c383a2

Browse files
authored
Fix setting breakpoints on AVX 256 instructions and other 32 byte immediate instructions (#54786)
1 parent 6cb345d commit 1c383a2

File tree

4 files changed

+62
-36
lines changed

4 files changed

+62
-36
lines changed

src/coreclr/debug/ee/controller.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@ bool DebuggerController::ApplyPatch(DebuggerControllerPatch *patch)
13931393
_ASSERTE(!"VirtualProtect of code page failed");
13941394
return false;
13951395
}
1396-
#endif // !defined(HOST_OSX) || !defined(HOST_ARM64)
1396+
#endif // !defined(HOST_OSX) || !defined(HOST_ARM64)
13971397
}
13981398
// TODO: : determine if this is needed for AMD64
13991399
#if defined(TARGET_X86) //REVISIT_TODO what is this?!
@@ -1496,7 +1496,7 @@ bool DebuggerController::UnapplyPatch(DebuggerControllerPatch *patch)
14961496
_ASSERTE(!"VirtualProtect of code page failed");
14971497
return false;
14981498
}
1499-
#endif // !defined(HOST_OSX) || !defined(HOST_ARM64)
1499+
#endif // !defined(HOST_OSX) || !defined(HOST_ARM64)
15001500
}
15011501
else
15021502
{
@@ -4352,6 +4352,7 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
43524352

43534353
m_pSharedPatchBypassBuffer = patch->GetOrCreateSharedPatchBypassBuffer();
43544354
BYTE* patchBypass = m_pSharedPatchBypassBuffer->PatchBypass;
4355+
LOG((LF_CORDB, LL_INFO10000, "DPS::DPS: Patch skip for opcode 0x%.4x at address %p buffer allocated at 0x%.8x\n", patch->opcode, patch->address, m_pSharedPatchBypassBuffer));
43554356

43564357
// Copy the instruction block over to the patch skip
43574358
// WARNING: there used to be an issue here because CopyInstructionBlock copied the breakpoint from the
@@ -4412,8 +4413,9 @@ DebuggerPatchSkip::DebuggerPatchSkip(Thread *thread,
44124413
}
44134414
else
44144415
{
4416+
_ASSERTE(m_instrAttrib.m_cOperandSize <= SharedPatchBypassBuffer::cbBufferBypass);
44154417
// Copy the data into our buffer.
4416-
memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, SharedPatchBypassBuffer::cbBufferBypass);
4418+
memcpy(bufferBypass, patch->address + m_instrAttrib.m_cbInstr + dwOldDisp, m_instrAttrib.m_cOperandSize);
44174419

44184420
if (m_instrAttrib.m_fIsWrite)
44194421
{
@@ -4901,11 +4903,14 @@ bool DebuggerPatchSkip::TriggerSingleStep(Thread *thread, const BYTE *ip)
49014903
break;
49024904

49034905
case 16:
4904-
memcpy(reinterpret_cast<void*>(targetFixup), bufferBypass, 16);
4906+
case 32:
4907+
memcpy(reinterpret_cast<void*>(targetFixup), bufferBypass, fixupSize);
49054908
break;
49064909

49074910
default:
4908-
_ASSERTE(!"bad operand size");
4911+
_ASSERTE(!"bad operand size. If you hit this and it was because we need to process instructions with larger \
4912+
relative immediates, make sure to update the SharedPatchBypassBuffer size, the DebuggerHeapExecutableMemoryAllocator, \
4913+
and structures depending on DBG_MAX_EXECUTABLE_ALLOC_SIZE.");
49094914
}
49104915
}
49114916
#endif

src/coreclr/debug/ee/controller.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,9 @@ class SharedPatchBypassBuffer
288288
// "PatchBypass" must be the first field of this class for alignment to be correct.
289289
BYTE PatchBypass[MAX_INSTRUCTION_LENGTH];
290290
#if defined(TARGET_AMD64)
291-
const static int cbBufferBypass = 0x10;
291+
// If you update this value, make sure that it fits in the data payload of a
292+
// DebuggerHeapExecutableMemoryChunk. This will need to be bumped to 0x40 for AVX 512 support.
293+
const static int cbBufferBypass = 0x20;
292294
BYTE BypassBuffer[cbBufferBypass];
293295

294296
UINT_PTR RipTargetFixup;

src/coreclr/debug/ee/debugger.cpp

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -365,10 +365,10 @@ void Debugger::DoNotCallDirectlyPrivateLock(void)
365365
//
366366
Thread * pThread;
367367
bool fIsCooperative;
368-
368+
369369
pThread = g_pEEInterface->GetThread();
370370
fIsCooperative = (pThread != NULL) && (pThread->PreemptiveGCDisabled());
371-
371+
372372
if (m_fShutdownMode && !fIsCooperative)
373373
{
374374
// The big fear is that some other random thread will take the debugger-lock and then block on something else,
@@ -16299,7 +16299,8 @@ void* DebuggerHeapExecutableMemoryAllocator::Allocate(DWORD numberOfBytes)
1629916299
}
1630016300
}
1630116301

16302-
return ChangePageUsage(pageToAllocateOn, chunkToUse, ChangePageUsageAction::ALLOCATE);
16302+
ASSERT(chunkToUse >= 1 && (uint)chunkToUse < CHUNKS_PER_DEBUGGERHEAP);
16303+
return GetPointerToChunkWithUsageUpdate(pageToAllocateOn, chunkToUse, ChangePageUsageAction::ALLOCATE);
1630316304
}
1630416305

1630516306
void DebuggerHeapExecutableMemoryAllocator::Free(void* addr)
@@ -16314,16 +16315,17 @@ void DebuggerHeapExecutableMemoryAllocator::Free(void* addr)
1631416315
int chunkNum = static_cast<DebuggerHeapExecutableMemoryChunk*>(addr)->data.chunkNumber;
1631516316

1631616317
// Sanity check: assert that the address really represents the start of a chunk.
16317-
ASSERT(((uint64_t)addr - (uint64_t)pageToFreeIn) % 64 == 0);
16318+
ASSERT(((uint64_t)addr - (uint64_t)pageToFreeIn) % EXPECTED_CHUNKSIZE == 0);
1631816319

16319-
ChangePageUsage(pageToFreeIn, chunkNum, ChangePageUsageAction::FREE);
16320+
GetPointerToChunkWithUsageUpdate(pageToFreeIn, chunkNum, ChangePageUsageAction::FREE);
1632016321
}
1632116322

1632216323
DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewPage()
1632316324
{
1632416325
void* newPageAddr = VirtualAlloc(NULL, sizeof(DebuggerHeapExecutableMemoryPage), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
1632516326

1632616327
DebuggerHeapExecutableMemoryPage *newPage = new (newPageAddr) DebuggerHeapExecutableMemoryPage;
16328+
CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
1632716329
newPage->SetNextPage(m_pages);
1632816330

1632916331
// Add the new page to the linked list of pages
@@ -16333,8 +16335,9 @@ DebuggerHeapExecutableMemoryPage* DebuggerHeapExecutableMemoryAllocator::AddNewP
1633316335

1633416336
bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHeapExecutableMemoryPage* page, /* _Out_ */ int* chunkToUse)
1633516337
{
16338+
CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
1633616339
uint64_t occupancy = page->GetPageOccupancy();
16337-
bool available = occupancy != UINT64_MAX;
16340+
bool available = occupancy != MAX_CHUNK_MASK;
1633816341

1633916342
if (!available)
1634016343
{
@@ -16348,13 +16351,13 @@ bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHea
1634816351

1634916352
if (chunkToUse)
1635016353
{
16351-
// Start i at 62 because first chunk is reserved
16352-
for (int i = 62; i >= 0; i--)
16354+
// skip the first bit, as that's used by the booking chunk.
16355+
for (int i = CHUNKS_PER_DEBUGGERHEAP - 2; i >= 0; i--)
1635316356
{
16354-
uint64_t mask = ((uint64_t)1 << i);
16357+
uint64_t mask = (1ull << i);
1635516358
if ((mask & occupancy) == 0)
1635616359
{
16357-
*chunkToUse = 64 - i - 1;
16360+
*chunkToUse = CHUNKS_PER_DEBUGGERHEAP - i - 1;
1635816361
break;
1635916362
}
1636016363
}
@@ -16363,12 +16366,12 @@ bool DebuggerHeapExecutableMemoryAllocator::CheckPageForAvailability(DebuggerHea
1636316366
return true;
1636416367
}
1636516368

16366-
void* DebuggerHeapExecutableMemoryAllocator::ChangePageUsage(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action)
16369+
void* DebuggerHeapExecutableMemoryAllocator::GetPointerToChunkWithUsageUpdate(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action)
1636716370
{
1636816371
ASSERT(action == ChangePageUsageAction::ALLOCATE || action == ChangePageUsageAction::FREE);
16372+
uint64_t mask = 1ull << (CHUNKS_PER_DEBUGGERHEAP - chunkNumber - 1);
1636916373

16370-
uint64_t mask = (uint64_t)0x1 << (64 - chunkNumber - 1);
16371-
16374+
CrstHolder execMemAllocCrstHolder(&m_execMemAllocMutex);
1637216375
uint64_t prevOccupancy = page->GetPageOccupancy();
1637316376
uint64_t newOccupancy = (action == ChangePageUsageAction::ALLOCATE) ? (prevOccupancy | mask) : (prevOccupancy ^ mask);
1637416377
page->SetPageOccupancy(newOccupancy);
@@ -16459,11 +16462,15 @@ HRESULT DebuggerHeap::Init(BOOL fExecutable)
1645916462
#endif
1646016463

1646116464
#ifndef HOST_WINDOWS
16462-
m_execMemAllocator = new (nothrow) DebuggerHeapExecutableMemoryAllocator();
16463-
ASSERT(m_execMemAllocator != NULL);
16464-
if (m_execMemAllocator == NULL)
16465+
m_execMemAllocator = NULL;
16466+
if (m_fExecutable)
1646516467
{
16466-
return E_OUTOFMEMORY;
16468+
m_execMemAllocator = new (nothrow) DebuggerHeapExecutableMemoryAllocator();
16469+
ASSERT(m_execMemAllocator != NULL);
16470+
if (m_execMemAllocator == NULL)
16471+
{
16472+
return E_OUTOFMEMORY;
16473+
}
1646716474
}
1646816475
#endif
1646916476

src/coreclr/debug/ee/debugger.h

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,7 +1047,12 @@ class DebuggerMethodInfo
10471047
// different part of the address space (not on the heap).
10481048
// ------------------------------------------------------------------------ */
10491049

1050-
#define DBG_MAX_EXECUTABLE_ALLOC_SIZE 48
1050+
constexpr uint64_t DBG_MAX_EXECUTABLE_ALLOC_SIZE=112;
1051+
constexpr uint64_t EXPECTED_CHUNKSIZE=128;
1052+
constexpr uint64_t DEBUGGERHEAP_PAGESIZE=4096;
1053+
constexpr uint64_t CHUNKS_PER_DEBUGGERHEAP=(DEBUGGERHEAP_PAGESIZE / EXPECTED_CHUNKSIZE);
1054+
constexpr uint64_t MAX_CHUNK_MASK=((1ull << CHUNKS_PER_DEBUGGERHEAP) - 1);
1055+
constexpr uint64_t BOOKKEEPING_CHUNK_MASK (1ull << (CHUNKS_PER_DEBUGGERHEAP - 1));
10511056

10521057
// Forward declaration
10531058
struct DebuggerHeapExecutableMemoryPage;
@@ -1060,7 +1065,7 @@ struct DebuggerHeapExecutableMemoryPage;
10601065
// for the page, and the remaining ones are DataChunks and are handed out
10611066
// by the allocator when it allocates memory.
10621067
// ------------------------------------------------------------------------ */
1063-
union DECLSPEC_ALIGN(64) DebuggerHeapExecutableMemoryChunk {
1068+
union DECLSPEC_ALIGN(EXPECTED_CHUNKSIZE) DebuggerHeapExecutableMemoryChunk {
10641069

10651070
struct DataChunk
10661071
{
@@ -1078,13 +1083,14 @@ union DECLSPEC_ALIGN(64) DebuggerHeapExecutableMemoryChunk {
10781083
DebuggerHeapExecutableMemoryPage *nextPage;
10791084

10801085
uint64_t pageOccupancy;
1086+
static_assert(CHUNKS_PER_DEBUGGERHEAP <= sizeof(pageOccupancy) * 8,
1087+
"Our interfaces assume the chunks in a page can be masken on this field");
10811088

10821089
} bookkeeping;
10831090

1084-
char _alignpad[64];
1091+
char _alignpad[EXPECTED_CHUNKSIZE];
10851092
};
1086-
1087-
static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == 64, "DebuggerHeapExecutableMemoryChunk is expect to be 64 bytes.");
1093+
static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == EXPECTED_CHUNKSIZE, "DebuggerHeapExecutableMemoryChunk is expect to be EXPECTED_CHUNKSIZE bytes.");
10881094

10891095
// ------------------------------------------------------------------------ */
10901096
// DebuggerHeapExecutableMemoryPage
@@ -1095,7 +1101,7 @@ static_assert(sizeof(DebuggerHeapExecutableMemoryChunk) == 64, "DebuggerHeapExec
10951101
// about which of the other chunks are used/free as well as a pointer to
10961102
// the next page.
10971103
// ------------------------------------------------------------------------ */
1098-
struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage
1104+
struct DECLSPEC_ALIGN(DEBUGGERHEAP_PAGESIZE) DebuggerHeapExecutableMemoryPage
10991105
{
11001106
inline DebuggerHeapExecutableMemoryPage* GetNextPage()
11011107
{
@@ -1115,24 +1121,25 @@ struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage
11151121

11161122
inline void SetPageOccupancy(uint64_t newOccupancy)
11171123
{
1118-
// Can't unset first bit of occupancy!
1119-
ASSERT((newOccupancy & 0x8000000000000000) != 0);
1120-
1124+
// Can't unset the bookmark chunk!
1125+
ASSERT((newOccupancy & BOOKKEEPING_CHUNK_MASK) != 0);
1126+
ASSERT(newOccupancy <= MAX_CHUNK_MASK);
11211127
ExecutableWriterHolder<DebuggerHeapExecutableMemoryPage> debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
11221128
debuggerHeapPageWriterHolder.GetRW()->chunks[0].bookkeeping.pageOccupancy = newOccupancy;
11231129
}
11241130

11251131
inline void* GetPointerToChunk(int chunkNum) const
11261132
{
1133+
ASSERT(chunkNum >= 0 && (uint)chunkNum < CHUNKS_PER_DEBUGGERHEAP);
11271134
return (char*)this + chunkNum * sizeof(DebuggerHeapExecutableMemoryChunk);
11281135
}
11291136

11301137
DebuggerHeapExecutableMemoryPage()
11311138
{
11321139
ExecutableWriterHolder<DebuggerHeapExecutableMemoryPage> debuggerHeapPageWriterHolder(this, sizeof(DebuggerHeapExecutableMemoryPage));
11331140

1134-
SetPageOccupancy(0x8000000000000000); // only the first bit is set.
1135-
for (uint8_t i = 1; i < sizeof(chunks)/sizeof(chunks[0]); i++)
1141+
SetPageOccupancy(BOOKKEEPING_CHUNK_MASK); // only the first bit is set.
1142+
for (uint8_t i = 1; i < CHUNKS_PER_DEBUGGERHEAP; i++)
11361143
{
11371144
ASSERT(i != 0);
11381145
debuggerHeapPageWriterHolder.GetRW()->chunks[i].data.startOfPage = this;
@@ -1141,8 +1148,13 @@ struct DECLSPEC_ALIGN(4096) DebuggerHeapExecutableMemoryPage
11411148
}
11421149

11431150
private:
1144-
DebuggerHeapExecutableMemoryChunk chunks[64];
1151+
DebuggerHeapExecutableMemoryChunk chunks[CHUNKS_PER_DEBUGGERHEAP];
1152+
static_assert(sizeof(chunks) == DEBUGGERHEAP_PAGESIZE,
1153+
"Expected DebuggerHeapExecutableMemoryPage to have DEBUGGERHEAP_PAGESIZE bytes worth of chunks.");
1154+
11451155
};
1156+
static_assert(sizeof(DebuggerHeapExecutableMemoryPage) == DEBUGGERHEAP_PAGESIZE,
1157+
"DebuggerHeapExecutableMemoryPage exceeded the expected size.");
11461158

11471159
// ------------------------------------------------------------------------ */
11481160
// DebuggerHeapExecutableMemoryAllocator class
@@ -1170,7 +1182,7 @@ class DebuggerHeapExecutableMemoryAllocator
11701182

11711183
DebuggerHeapExecutableMemoryPage* AddNewPage();
11721184
bool CheckPageForAvailability(DebuggerHeapExecutableMemoryPage* page, /* _Out_ */ int* chunkToUse);
1173-
void* ChangePageUsage(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action);
1185+
void* GetPointerToChunkWithUsageUpdate(DebuggerHeapExecutableMemoryPage* page, int chunkNumber, ChangePageUsageAction action);
11741186

11751187
private:
11761188
// Linked list of pages that have been allocated

0 commit comments

Comments
 (0)