// DacFreeVirtual - Free memory from the target process // Note: this is only available to clients supporting the legacy // ICLRDataTarget2 interface. This is not currently used. HRESULT DacFreeVirtual(TADDR mem, ULONG32 size, ULONG32 typeFlags, bool throwEx) { if (!g_dacImpl) { DacError(E_UNEXPECTED); UNREACHABLE(); } ICLRDataTarget2 * pTarget2 = g_dacImpl->GetLegacyTarget2(); if (pTarget2 == NULL) { DacError(E_NOTIMPL); UNREACHABLE(); } HRESULT status = pTarget2->FreeVirtual( TO_CDADDR(mem), size, typeFlags); if (status != S_OK && throwEx) { DacError(status); UNREACHABLE(); } return status; }
HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr) { HRESULT Status = S_OK; ULONG Size = GetILSize(ilAddr); if (Size == 0) { ExtOut("error decoding IL\n"); return Status; } ExtOut("ilAddr = %p\n", (ULONG64) ilAddr); // Read the memory into a local buffer ArrayHolder<BYTE> pArray = new BYTE[Size]; Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), pArray, Size, NULL); if (Status != S_OK) { ExtOut("Failed to read memory\n"); return Status; } DecodeIL(pImport, pArray, Size); return Status; }
// DacAllocVirtual - Allocate memory from the target process // Note: this is only available to clients supporting the legacy // ICLRDataTarget2 interface. It's currently used by SOS for notification tables. HRESULT DacAllocVirtual(TADDR addr, ULONG32 size, ULONG32 typeFlags, ULONG32 protectFlags, bool throwEx, TADDR* mem) { if (!g_dacImpl) { DacError(E_UNEXPECTED); UNREACHABLE(); } ICLRDataTarget2 * pTarget2 = g_dacImpl->GetLegacyTarget2(); if (pTarget2 == NULL) { DacError(E_NOTIMPL); UNREACHABLE(); } CLRDATA_ADDRESS cdaMem; HRESULT status = pTarget2->AllocVirtual( TO_CDADDR(addr), size, typeFlags, protectFlags, &cdaMem); if (status != S_OK) { if (throwEx) { DacError(status); UNREACHABLE(); } return status; } *mem = CLRDATA_ADDRESS_TO_TADDR(cdaMem); return S_OK; }
// impl of interface method ICorDebugDataTarget::ReadVirtual HRESULT STDMETHODCALLTYPE DataTargetAdapter::ReadVirtual( CORDB_ADDRESS address, PBYTE pBuffer, ULONG32 cbRequestSize, ULONG32 * pcbRead) { SUPPORTS_DAC_HOST_ONLY; CLRDATA_ADDRESS cdAddr = TO_CDADDR(address); return m_pLegacyTarget->ReadVirtual(cdAddr, pBuffer, cbRequestSize, pcbRead); }
bool Object::VerifyMemberFields(TADDR pMT, TADDR obj, WORD &numInstanceFields) { DacpMethodTableData vMethTable; if (FAILED(vMethTable.Request(g_sos, pMT))) return false; // Recursively verify the parent (this updates numInstanceFields) if (vMethTable.ParentMethodTable) { if (!VerifyMemberFields(TO_TADDR(vMethTable.ParentMethodTable), obj, numInstanceFields)) return false; } DacpMethodTableFieldData vMethodTableFields; // Verify all fields on the object. CLRDATA_ADDRESS dwAddr = vMethodTableFields.FirstField; DacpFieldDescData vFieldDesc; while (numInstanceFields < vMethodTableFields.wNumInstanceFields) { CheckInterrupt(); if (FAILED(vFieldDesc.Request(g_sos, dwAddr))) return false; if (vFieldDesc.Type >= ELEMENT_TYPE_MAX) return false; dwAddr = vFieldDesc.NextField; if (!vFieldDesc.bIsStatic) { numInstanceFields++; TADDR dwTmp = TO_TADDR(obj + vFieldDesc.dwOffset + sizeof(BaseObject)); if (vFieldDesc.Type == ELEMENT_TYPE_CLASS) { // Is it a valid object? if (FAILED(MOVE(dwTmp, dwTmp))) return false; if (dwTmp != NULL) { DacpObjectData objData; if (FAILED(objData.Request(g_sos, TO_CDADDR(dwTmp)))) return false; } } } } return true; }
TADDR Object::GetComponentMT() const { if (mMT != NULL && mMT != sos::MethodTable::GetArrayMT()) return NULL; DacpObjectData objData; if (FAILED(objData.Request(g_sos, TO_CDADDR(mAddress)))) sos::Throw<DataRead>("Failed to request object data for %s.", DMLListNearObj(mAddress)); if (mMT == NULL) mMT = TO_TADDR(objData.MethodTable) & ~3; return TO_TADDR(objData.ElementTypeHandle); }
bool Object::IsValid(TADDR address, bool verifyFields) { DacpObjectData objectData; if (FAILED(objectData.Request(g_sos, TO_CDADDR(address)))) return false; if (verifyFields && objectData.MethodTable != g_special_usefulGlobals.FreeMethodTable && !MethodTable::IsZombie(TO_TADDR(objectData.MethodTable))) { return VerifyMemberFields(TO_TADDR(objectData.MethodTable), address); } return true; }
ULONG GetILSize(DWORD_PTR ilAddr) { ULONG uRet = 0; // workaround: read enough bytes at ilAddr to presumably get the entire header. // Could be error prone. static BYTE headerArray[1024]; HRESULT Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), headerArray, sizeof(headerArray), NULL); if (SUCCEEDED(Status)) { COR_ILMETHOD_DECODER header((COR_ILMETHOD *)headerArray); // uRet = header.GetHeaderSize(); uRet = header.GetOnDiskSize((COR_ILMETHOD *)headerArray); } return uRet; }
// impl of interface method ICorDebugMutableDataTarget::WriteVirtual HRESULT STDMETHODCALLTYPE DataTargetAdapter::WriteVirtual( CORDB_ADDRESS address, const BYTE * pBuffer, ULONG32 cbRequestSize) { SUPPORTS_DAC_HOST_ONLY; CLRDATA_ADDRESS cdAddr = TO_CDADDR(address); ULONG32 cbWritten = 0; HRESULT hr = S_OK; hr = m_pLegacyTarget->WriteVirtual(cdAddr, const_cast<BYTE *>(pBuffer), cbRequestSize, &cbWritten); if (SUCCEEDED(hr) && cbWritten != cbRequestSize) { // This shouldn't happen - existing data target implementations make writes atomic (eg. // WriteProcessMemory), even though that isn't strictly required by the old interface. // If this does happen, we technically leave the process in an inconsistent state, and we make no // attempt to recover from that here. _ASSERTE_MSG(false, "Legacy data target WriteVirtual partial write - target left in inconsistent state"); return HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY); } return hr; }
void UnassemblyUnmanaged(DWORD_PTR IP, BOOL bSuppressLines) { char filename[MAX_PATH_FNAME+1]; char line[256]; int lcount = 10; ULONG linenum = 0; ULONG64 Displacement = 0; BOOL fLineAvailable = FALSE; ULONG64 vIP = 0; if (!bSuppressLines) { ReloadSymbolWithLineInfo(); fLineAvailable = SUCCEEDED (g_ExtSymbols->GetLineByOffset (TO_CDADDR(IP), &linenum, filename, MAX_PATH_FNAME+1, NULL, &Displacement)); } ULONG FileLines = 0; ArrayHolder<ULONG64> Buffer = NULL; if (fLineAvailable) { g_ExtSymbols->GetSourceFileLineOffsets (filename, NULL, 0, &FileLines); if (FileLines == 0xFFFFFFFF || FileLines == 0) fLineAvailable = FALSE; } if (fLineAvailable) { Buffer = new ULONG64[FileLines]; if (Buffer == NULL) fLineAvailable = FALSE; } if (!fLineAvailable) { vIP = TO_CDADDR(IP); // There is no line info. Just disasm the code. while (lcount-- > 0) { if (IsInterrupt()) return; g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP); ExtOut (line); } return; } g_ExtSymbols->GetSourceFileLineOffsets (filename, Buffer, FileLines, NULL); int beginLine = 0; int endLine = 0; int lastLine; linenum --; for (lastLine = linenum; lastLine >= 0; lastLine --) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { g_ExtSymbols->GetNameByOffset(Buffer[lastLine],NULL,0,NULL,&Displacement); if (Displacement == 0) { beginLine = lastLine; break; } } } if (lastLine < 0) { int n = lcount / 2; lastLine = linenum-1; beginLine = lastLine; while (lastLine >= 0) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { beginLine = lastLine; n --; if (n == 0) { break; } } lastLine --; } } while (beginLine > 0 && Buffer[beginLine-1] == DEBUG_INVALID_OFFSET) { if (IsInterrupt()) return; beginLine --; } int endOfFunc = 0; for (lastLine = linenum+1; (ULONG)lastLine < FileLines; lastLine ++) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { g_ExtSymbols->GetNameByOffset(Buffer[lastLine],NULL,0,NULL,&Displacement); if (Displacement == 0) { endLine = lastLine; break; } endOfFunc = lastLine; } } if ((ULONG)lastLine == FileLines) { int n = lcount / 2; lastLine = linenum+1; endLine = lastLine; while ((ULONG)lastLine < FileLines) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { endLine = lastLine; n --; if (n == 0) { break; } } lastLine ++; } } PVOID MappedBase = NULL; ULONG MappedSize = 0; class ToUnmap { PVOID *m_Base; public: ToUnmap (PVOID *base) :m_Base(base) {} ~ToUnmap () { if (*m_Base) { UnmapViewOfFile (*m_Base); *m_Base = NULL; } } }; ToUnmap toUnmap(&MappedBase); #define MAX_SOURCE_PATH 1024 char Found[MAX_SOURCE_PATH]; char *pFile; if (g_ExtSymbols->FindSourceFile(0, filename, DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH, NULL, Found, sizeof(Found), NULL) != S_OK) { pFile = filename; } else { MappedBase = GenOpenMapping ( Found, &MappedSize ); pFile = Found; } lastLine = beginLine; char *pFileCh = (char*)MappedBase; if (MappedBase) { ExtOut ("%s\n", pFile); int n = beginLine; while (n > 0) { while (!(pFileCh[0] == '\r' && pFileCh[1] == 0xa)) { if (IsInterrupt()) return; pFileCh ++; } pFileCh += 2; n --; } } char filename1[MAX_PATH_FNAME+1]; for (lastLine = beginLine; lastLine < endLine; lastLine ++) { if (IsInterrupt()) return; if (MappedBase) { ExtOut ("%4d ", lastLine+1); pFileCh = PrintOneLine (pFileCh, (char*)MappedBase+MappedSize); } if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { if (MappedBase == 0) { ExtOut (">>> %s:%d\n", pFile, lastLine+1); } vIP = Buffer[lastLine]; ULONG64 vNextLineIP; int i; for (i = lastLine + 1; (ULONG)i < FileLines && Buffer[i] == DEBUG_INVALID_OFFSET; i ++) { if (IsInterrupt()) return; } if ((ULONG)i == FileLines) { vNextLineIP = 0; } else vNextLineIP = Buffer[i]; while (1) { if (IsInterrupt()) return; g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP); ExtOut (line); if (vIP > vNextLineIP || vNextLineIP - vIP > 40) { if (FAILED (g_ExtSymbols->GetLineByOffset (vIP, &linenum, filename1, MAX_PATH_FNAME+1, NULL, &Displacement))) { if (lastLine != endOfFunc) { break; } if (strstr (line, "ret") || strstr (line, "jmp")) { break; } } if (linenum != (ULONG)lastLine+1 || strcmp (filename, filename1)) { break; } } else if (vIP == vNextLineIP) { break; } } } } }