#pragma warning(push) #pragma warning(disable:4146) #pragma warning(disable:4996) #pragma warning(disable:4800) #pragma warning(disable:4244) #include "DbgModule.h" #include "DWARFInfo.h" #include #include #include #include #include #include #include #include "WinDebugger.h" #include "DebugManager.h" #include "DebugTarget.h" #include "COFFData.h" #include "Compiler/BfDemangler.h" #include "BeefySysLib/util/Hash.h" #include "BeefySysLib/util/BeefPerf.h" #include "DbgSymSrv.h" #include "MiniDumpDebugger.h" #pragma warning(pop) #pragma warning(disable:4996) #include "BeefySysLib/util/AllocDebug.h" USING_NS_BF_DBG; void SetBreakpoint(int64_t address); NS_BF_DBG_BEGIN #ifdef BF_DBG_32 typedef PEOptionalHeader32 PEOptionalHeader; typedef PE_NTHeaders32 PE_NTHeaders; #else typedef PEOptionalHeader64 PEOptionalHeader; typedef PE_NTHeaders64 PE_NTHeaders; #endif #define GET(T) *((T*)(data += sizeof(T)) - 1) #define GET_FROM(ptr, T) *((T*)(ptr += sizeof(T)) - 1) ////////////////////////////////////////////////////////////////////////// DbgCompileUnit::DbgCompileUnit(DbgModule* dbgModule) { mDbgModule = dbgModule; mLanguage = DbgLanguage_Unknown; mGlobalBlock = mDbgModule->mAlloc.Alloc(); mGlobalType = mDbgModule->mAlloc.Alloc(); mGlobalType->mTypeCode = DbgType_Root; mGlobalType->mPriority = DbgTypePriority_Primary_Explicit; mGlobalType->mCompileUnit = this; mLowPC = (addr_target)-1; mHighPC = 0; //mDoPrimaryRemapping = true; mNeedsLineDataFixup = true; mWasHotReplaced = false; mIsMaster = false; } ////////////////////////////////////////////////////////////////////////// addr_target DbgLineDataEx::GetAddress() { return mSubprogram->GetLineAddr(*mLineData); } DbgSrcFile* DbgLineDataEx::GetSrcFile() { auto inlineRoot = mSubprogram->GetRootInlineParent(); return inlineRoot->mLineInfo->mContexts[mLineData->mCtxIdx].mSrcFile; } addr_target DbgSubprogram::GetLineAddr(const DbgLineData& lineData) { return (addr_target)(lineData.mRelAddress + mCompileUnit->mDbgModule->mImageBase); } DbgSubprogram* DbgSubprogram::GetLineInlinee(const DbgLineData& lineData) { auto inlineRoot = GetRootInlineParent(); return inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx].mInlinee; } DbgSrcFile* DbgSubprogram::GetLineSrcFile(const DbgLineData& lineData) { auto inlineRoot = GetRootInlineParent(); return inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx].mSrcFile; } bool DbgSubprogram::HasValidLines() { auto inlineRoot = GetRootInlineParent(); for (int lineIdx = 0; lineIdx < (int)inlineRoot->mLineInfo->mLines.size(); lineIdx++) { auto& lineInfo = inlineRoot->mLineInfo->mLines[lineIdx]; if (lineInfo.mColumn >= 0) return true; } return false; } void DbgSubprogram::PopulateSubprogram() { if (mDeferredInternalsSize == 0) return; mCompileUnit->mDbgModule->PopulateSubprogram(this); } ////////////////////////////////////////////////////////////////////////// DbgLineDataBuilder::DbgLineDataBuilder(DbgModule* dbgModule) { mDbgModule = dbgModule; mCurSubprogram = NULL; mCurRecord = NULL; } DbgLineData* DbgLineDataBuilder::Add(DbgCompileUnit* compileUnit, DbgLineData& lineData, DbgSrcFile* srcFile, DbgSubprogram* inlinee) { addr_target address = (addr_target)(lineData.mRelAddress + mDbgModule->mImageBase); if ((compileUnit->mLowPC != (addr_target)-1) && ((address < (addr_target)compileUnit->mLowPC) || (address >= (addr_target)compileUnit->mHighPC))) return NULL; if ((mCurSubprogram == NULL) || (address < mCurSubprogram->mBlock.mLowPC) || (address >= mCurSubprogram->mBlock.mHighPC)) { DbgSubprogramMapEntry* mapEntry = mDbgModule->mDebugTarget->mSubprogramMap.Get(address, DBG_MAX_LOOKBACK); if (mapEntry != NULL) { mCurSubprogram = mapEntry->mEntry; if (address > mCurSubprogram->mBlock.mHighPC) mCurSubprogram = NULL; if (mCurSubprogram != NULL) { SubprogramRecord** recordPtr = NULL; if (mRecords.TryAdd(mCurSubprogram, NULL, &recordPtr)) { // It's not too expensive to over-reserve here, because these are just temporary structures that get copied // exactly sized when we Commit mCurRecord = mAlloc.Alloc(); *recordPtr = mCurRecord; mCurRecord->mContexts.mAlloc = &mAlloc; mCurRecord->mContexts.Reserve(16); mCurRecord->mLines.mAlloc = &mAlloc; mCurRecord->mLines.Reserve(128); mCurRecord->mCurContext = -1; mCurRecord->mHasInlinees = false; } else mCurRecord = *recordPtr; } else mCurRecord = NULL; } } if (mCurSubprogram == NULL) return NULL; bool needsNewCtx = false; if (mCurRecord->mCurContext == -1) { needsNewCtx = true; } else { auto& curContext = mCurRecord->mContexts[mCurRecord->mCurContext]; if ((curContext.mInlinee != inlinee) || (curContext.mSrcFile != srcFile)) { needsNewCtx = true; for (int ctxIdx = 0; ctxIdx < (int)mCurRecord->mContexts.size(); ctxIdx++) { auto& ctx = mCurRecord->mContexts[ctxIdx]; if ((ctx.mInlinee == inlinee) && (ctx.mSrcFile == srcFile)) { needsNewCtx = false; mCurRecord->mCurContext = ctxIdx; break; } } } } if (needsNewCtx) { DbgLineInfoCtx ctx; ctx.mInlinee = inlinee; ctx.mSrcFile = srcFile; if (inlinee != NULL) mCurRecord->mHasInlinees = true; mCurRecord->mContexts.Add(ctx); mCurRecord->mCurContext = (int)mCurRecord->mContexts.size() - 1; } lineData.mCtxIdx = mCurRecord->mCurContext; if ((mCurSubprogram->mPrologueSize > 0) && (mCurRecord->mLines.size() == 1) && (inlinee == NULL)) { auto& firstLine = mCurRecord->mLines[0]; auto dbgStartAddr = firstLine.mRelAddress + mCurSubprogram->mPrologueSize; if (lineData.mRelAddress != dbgStartAddr) { DbgLineData dbgStartLine = firstLine; dbgStartLine.mRelAddress = dbgStartAddr; mCurRecord->mLines.Add(dbgStartLine); } firstLine.mColumn = -2; // Marker for 'in prologue' } if (inlinee != NULL) { if (inlinee->mInlineeInfo->mFirstLineData.mRelAddress == 0) inlinee->mInlineeInfo->mFirstLineData = lineData; inlinee->mInlineeInfo->mLastLineData = lineData; } mCurRecord->mLines.Add(lineData); return &mCurRecord->mLines.back(); } void DbgLineDataBuilder::Commit() { HashSet usedSrcFiles; for (auto& recordKV : mRecords) { auto dbgSubprogram = recordKV.mKey; auto record = recordKV.mValue; usedSrcFiles.Clear(); for (auto& ctx : record->mContexts) { if (usedSrcFiles.Add(ctx.mSrcFile)) { ctx.mSrcFile->mLineDataRefs.Add(dbgSubprogram); } } for (int lineIdx = 0; lineIdx < (int)record->mLines.size() - 1; lineIdx++) { auto& lineData = record->mLines[lineIdx]; auto& nextLineData = record->mLines[lineIdx + 1]; if ((lineData.mContribSize == 0) && (lineData.mCtxIdx == nextLineData.mCtxIdx)) { lineData.mContribSize = (uint32)(nextLineData.mRelAddress - lineData.mRelAddress); } bool sameInliner = lineData.mCtxIdx == nextLineData.mCtxIdx; if (!sameInliner) { auto ctx = record->mContexts[lineData.mCtxIdx]; auto nextCtx = record->mContexts[lineData.mCtxIdx]; sameInliner = ctx.mInlinee == nextCtx.mInlinee; } if ((sameInliner) && (lineData.mRelAddress + lineData.mContribSize < nextLineData.mRelAddress)) { auto ctx = record->mContexts[lineData.mCtxIdx]; if (ctx.mInlinee != NULL) ctx.mInlinee->mHasLineAddrGaps = true; } } DbgLineData* lastLine = NULL; for (int lineIdx = 0; lineIdx < (int)record->mLines.size(); lineIdx++) { auto& lineData = record->mLines[lineIdx]; if (lineData.mContribSize == 0) { auto ctx = record->mContexts[lineData.mCtxIdx]; if (ctx.mInlinee == NULL) lastLine = &lineData; } } if (lastLine != NULL) lastLine->mContribSize = (uint32)(dbgSubprogram->mBlock.mHighPC - (mDbgModule->mImageBase + lastLine->mRelAddress)); BF_ASSERT(dbgSubprogram->mLineInfo == NULL); dbgSubprogram->mLineInfo = mDbgModule->mAlloc.Alloc(); dbgSubprogram->mLineInfo->mLines.CopyFrom(&record->mLines[0], (int)record->mLines.size(), mDbgModule->mAlloc); BfSizedArray contexts; contexts.CopyFrom(&record->mContexts[0], (int)record->mContexts.size(), mDbgModule->mAlloc); dbgSubprogram->mLineInfo->mContexts = contexts.mVals; dbgSubprogram->mLineInfo->mHasInlinees = record->mHasInlinees; } } ////////////////////////////////////////////////////////////////////////// static const char* DataGetString(const uint8*& data) { const char* prevVal = (const char*)data; while (*data != 0) data++; data++; return prevVal; } struct AbstractOriginEntry { public: int mClassType; DbgDebugData* mDestination; DbgDebugData* mAbstractOrigin; private: AbstractOriginEntry() { } public: static AbstractOriginEntry Create(int classType, DbgDebugData* destination, DbgDebugData* abstractOrigin) { AbstractOriginEntry abstractOriginEntry; abstractOriginEntry.mClassType = classType; abstractOriginEntry.mDestination = destination; abstractOriginEntry.mAbstractOrigin = abstractOrigin; return abstractOriginEntry; } void Replace() { if (mClassType == DbgSubprogram::ClassType) { DbgSubprogram* destSubprogram = (DbgSubprogram*)mDestination; DbgSubprogram* originSubprogram = (DbgSubprogram*)mAbstractOrigin; if (destSubprogram->mName == NULL) { destSubprogram->mName = originSubprogram->mName; destSubprogram->mParentType = originSubprogram->mParentType; } destSubprogram->mHasThis = originSubprogram->mHasThis; if (destSubprogram->mFrameBaseData == NULL) { destSubprogram->mFrameBaseData = originSubprogram->mFrameBaseData; destSubprogram->mFrameBaseLen = originSubprogram->mFrameBaseLen; } destSubprogram->mReturnType = originSubprogram->mReturnType; auto originItr = originSubprogram->mParams.begin(); for (auto destParam : destSubprogram->mParams) { DbgVariable* originParam = *originItr; if (originParam != NULL) { if (destParam->mName == NULL) destParam->mName = originParam->mName; if (destParam->mType == NULL) destParam->mType = originParam->mType; } ++originItr; } //BF_ASSERT(originItr == originSubprogram->mParams.end()); } else if (mClassType == DbgVariable::ClassType) { DbgVariable* destVariable = (DbgVariable*)mDestination; DbgVariable* originVariable = (DbgVariable*)mAbstractOrigin; if (destVariable->mName == NULL) destVariable->mName = originVariable->mName; if (destVariable->mType == NULL) destVariable->mType = originVariable->mType; } else { BF_FATAL("Unhandled"); } } }; NS_BF_DBG_END ////////////////////////////////////////////////////////////////////////// String DbgSubprogram::ToString() { if ((mInlineeInfo != NULL) && (mInlineeInfo->mInlineeId != 0)) mCompileUnit->mDbgModule->FixupInlinee(this); PopulateSubprogram(); String str; if (mCheckedKind == BfCheckedKind_Checked) str += "[Checked] "; else if (mCheckedKind == BfCheckedKind_Unchecked) str += "[Unchecked] "; auto language = GetLanguage(); if (mName == NULL) { if (mLinkName[0] == '<') return mLinkName; str = BfDemangler::Demangle(StringImpl::MakeRef(mLinkName), language); // Strip off the params since we need to generate those ourselves int parenPos = (int)str.IndexOf('('); if (parenPos != -1) str = str.Substring(0, parenPos); } else if (mHasQualifiedName) { const char* cPtr = mName; if (strncmp(cPtr, "_bf::", 5) == 0) { cPtr += 5; for ( ; true; cPtr++) { char c = *cPtr; if (c == 0) break; if ((c == '_') && (cPtr[-1] == ':')) { if (strcmp(cPtr, "__BfCtor") == 0) { str += "this"; break; } if (strcmp(cPtr, "__BfStaticCtor") == 0) { str += "this$static"; break; } if (strcmp(cPtr, "__BfCtorClear") == 0) { str += "this$clear"; break; } } if ((c == ':') && (cPtr[1] == ':')) { str.Append('.'); cPtr++; } else str.Append(c); } } else str += mName; } else { if (mParentType != NULL) { str += mParentType->ToString(); if (!str.empty()) { if (language == DbgLanguage_Beef) str += "."; else str += "::"; } } if ((language == DbgLanguage_Beef) && (mParentType != NULL) && (mParentType->mTypeName != NULL) && (strcmp(mName, mParentType->mTypeName) == 0)) str += "this"; else if ((language == DbgLanguage_Beef) && (mName[0] == '~')) str += "~this"; else if (strncmp(mName, "_bf::", 5) == 0) str += mName + 5; else { bool handled = false; if ((language == DbgLanguage_Beef) && (mName[0] == '_')) { if (strcmp(mName, "__BfCtor") == 0) { str += "this"; handled = true; } else if (strcmp(mName, "__BfStaticCtor") == 0) { str += "this"; handled = true; } else if (strcmp(mName, "__BfCtorClear") == 0) { str += "this$clear"; handled = true; } } if (!handled) str += mName; } } //if (mTemplateName != NULL) //str += mTemplateName; if (str.empty()) str += "`anon"; if ((str[str.length() - 1] == '!') || (str[0] == '<')) { if (language == DbgLanguage_Beef) { // It's a mixin - assert that there's no params //BF_ASSERT(mParams.Size() == 0); } //return str; } str += "("; bool showedParam = false; int i = 0; for (auto variable : mParams) { if ((variable->mName != NULL) && (strcmp(variable->mName, "this") == 0)) continue; if (showedParam) str += ", "; if (variable->mType != NULL) { auto varType = variable->mType; if (varType->mTypeCode == DbgType_Const) varType = varType->mTypeParam; if (variable->mSigNoPointer) { BF_ASSERT(varType->IsPointer()); varType = varType->mTypeParam; } str += varType->ToString(language); if (variable->mName != NULL) str += " "; } if (variable->mName != NULL) str += variable->mName; showedParam = true; i++; } str += ")"; return str; } // For inlined subprograms, the "root" inliner means the bottom-most non-inlined function. This subprogram contains // all the line data for it's own non-inlined instructions, PLUS line data for all inlined functions that it calls. // The inlined functions has empty mLineInfo structures. // // When we pass a non-NULL value into inlinedSubprogram, we are requesting to ONLY return lines that were emitted from // that subprogram (inlined or not). // // If we call FindClosestLine on an inlined subprogram, we only want results of functions that are inside or inlined by // the 'this' subprogram. Thus, we do a "get any line" call on the root inliner and then filter the results based // on whether they are relevant. DbgLineData* DbgSubprogram::FindClosestLine(addr_target addr, DbgSubprogram** inlinedSubprogram, DbgSrcFile** srcFile, int* outLineIdx) { if (mLineInfo == NULL) { if (mInlineeInfo == NULL) return NULL; if ((inlinedSubprogram != NULL) && (*inlinedSubprogram != NULL)) { // Keep explicit inlinee requirement return mInlineeInfo->mRootInliner->FindClosestLine(addr, inlinedSubprogram, srcFile, outLineIdx); } else { DbgSubprogram* rootInlinedSubprogram = NULL; auto result = mInlineeInfo->mRootInliner->FindClosestLine(addr, &rootInlinedSubprogram, srcFile, outLineIdx); if (result == NULL) return NULL; if (rootInlinedSubprogram == NULL) // Do not allow root parent, as we cannot be a parent to the root parent (duh) return NULL; // We need to check to see if we are a parent of the found line auto checkSubprogram = rootInlinedSubprogram; while ((checkSubprogram != NULL) && (checkSubprogram->mInlineeInfo != NULL)) { if (checkSubprogram == this) { if (inlinedSubprogram != NULL) *inlinedSubprogram = rootInlinedSubprogram; return result; } checkSubprogram = checkSubprogram->mInlineeInfo->mInlineParent; } return NULL; } } // Binary search - lineData is sorted int first = 0; int last = (int)mLineInfo->mLines.mSize - 1; int middle = (first + last) / 2; int useIdx = -1; while (first <= last) { addr_target midAddr = (addr_target)(mLineInfo->mLines.mVals[middle].mRelAddress + mCompileUnit->mDbgModule->mImageBase); if (midAddr < addr) first = middle + 1; else if (midAddr == addr) { useIdx = middle; break; } else last = middle - 1; middle = (first + last) / 2; } if (useIdx == -1) useIdx = last; if (last == -1) return NULL; // If we have lines with the same addr, take the more inner one while (true) { auto lineData = &mLineInfo->mLines.mVals[useIdx]; if (useIdx + 1 < mLineInfo->mLines.mSize) { auto peekNext = &mLineInfo->mLines.mVals[useIdx + 1]; if (lineData->mRelAddress != peekNext->mRelAddress) break; useIdx++; } else { break; } } while (true) { auto lineData = &mLineInfo->mLines.mVals[useIdx]; if (addr < lineData->mRelAddress + lineData->mContribSize + mCompileUnit->mDbgModule->mImageBase) { auto& ctx = mLineInfo->mContexts[lineData->mCtxIdx]; if (srcFile != NULL) *srcFile = ctx.mSrcFile; if (inlinedSubprogram != NULL) { auto subprogram = (ctx.mInlinee != NULL) ? ctx.mInlinee : this; if (*inlinedSubprogram != NULL) { // Strictness check if (subprogram == *inlinedSubprogram) { if (outLineIdx != NULL) *outLineIdx = useIdx; return lineData; } } else { *inlinedSubprogram = subprogram; if (outLineIdx != NULL) *outLineIdx = useIdx; return lineData; } } else { if (outLineIdx != NULL) *outLineIdx = useIdx; return lineData; } } // Hope we can find an earlier entry whose "contribution" is still valid if (--useIdx < 0) break; } return NULL; } DbgType* DbgSubprogram::GetParent() { if ((mParentType == NULL) && (mCompileUnit != NULL)) mCompileUnit->mDbgModule->MapCompileUnitMethods(mCompileUnit); return mParentType; } DbgType* DbgSubprogram::GetTargetType() { if (!mHasThis) return mParentType; auto thisType = mParams.mHead->mType; if (thisType == NULL) return mParentType; if (thisType->IsPointer()) return thisType->mTypeParam; return thisType; } DbgLanguage DbgSubprogram::GetLanguage() { if (mParentType != NULL) return mParentType->GetLanguage(); if (mCompileUnit->mLanguage != DbgLanguage_Unknown) return mCompileUnit->mLanguage; return DbgLanguage_C; // Parent type would have been set for Beef, so it must be C } bool DbgSubprogram::Equals(DbgSubprogram* checkMethod, bool allowThisMismatch) { if ((mLinkName != NULL) && (checkMethod->mLinkName != NULL)) { return strcmp(mLinkName, checkMethod->mLinkName) == 0; } if (strcmp(mName, checkMethod->mName) != 0) return false; if (mHasThis != checkMethod->mHasThis) return false; int paramIdx = 0; auto param = mParams.mHead; auto checkParam = checkMethod->mParams.mHead; while ((param != NULL) && (checkParam != NULL)) { if ((paramIdx == 0) && (allowThisMismatch)) { // Allow } else if ((param->mType != checkParam->mType) && (!param->mType->Equals(checkParam->mType))) return false; param = param->mNext; checkParam = checkParam->mNext; paramIdx++; } if ((param != NULL) || (checkParam != NULL)) return false; if (!mReturnType->Equals(checkMethod->mReturnType)) return false; return true; } int DbgSubprogram::GetParamCount() { int paramCount = mParams.Size(); if (mHasThis) paramCount--; return paramCount; } String DbgSubprogram::GetParamName(int paramIdx) { auto param = mParams[paramIdx]; if (param->mName != NULL) { String name = "'"; name += param->mName; name += "'"; return name; } return StrFormat("%d", paramIdx + 1); } bool DbgSubprogram::IsGenericMethod() { if (mName == NULL) return false; for (const char* cPtr = mName; true; cPtr++) { char c = *cPtr; if (c == '\0') break; if (c == '<') return true; } return false; } bool DbgSubprogram::ThisIsSplat() { if (mBlock.mVariables.mHead == NULL) return false; return strncmp(mBlock.mVariables.mHead->mName, "$this$", 6) == 0; } bool DbgSubprogram::IsLambda() { if (mName == NULL) return false; return StringView(mName).Contains('$'); } ////////////////////////////////////////////////////////////////////////// DbgSubprogram::~DbgSubprogram() { BfLogDbg("DbgSubprogram::~DbgSubprogram %p\n", this); } //////////////////// bool DbgSrcFile::IsBeef() { int dotPos = (int)mFilePath.LastIndexOf('.'); if (dotPos == -1) return false; const char* ext = mFilePath.c_str() + dotPos; // The ".cs" is legacy. Remove that eventually. return (stricmp(ext, ".bf") == 0) || (stricmp(ext, ".cs") == 0); } DbgSrcFile::~DbgSrcFile() { for (auto replacedLineInfo : mHotReplacedDbgLineInfo) delete replacedLineInfo; } void DbgSrcFile::RemoveDeferredRefs(DbgModule* debugModule) { for (int deferredIdx = 0; deferredIdx < (int)mDeferredRefs.size(); ) { if (mDeferredRefs[deferredIdx].mDbgModule == debugModule) { // Fast remove mDeferredRefs[deferredIdx] = mDeferredRefs.back(); mDeferredRefs.pop_back(); } else deferredIdx++; } } void DbgSrcFile::RemoveLines(DbgModule* debugModule) { if (!mHasLineDataFromMultipleModules) { // Fast-out case mLineDataRefs.Clear(); mFirstLineDataDbgModule = NULL; return; } for (int idx = 0; idx < (int)mLineDataRefs.size(); idx++) { auto dbgSubprogram = mLineDataRefs[idx]; if (dbgSubprogram->mCompileUnit->mDbgModule == debugModule) { mLineDataRefs.RemoveAtFast(idx); idx--; } } } void DbgSrcFile::RemoveLines(DbgModule* debugModule, DbgSubprogram* dbgSubprogram, bool isHotReplaced) { debugModule->mDebugTarget->mPendingSrcFileRehup.Add(this); if (isHotReplaced) { int vecIdx = dbgSubprogram->mCompileUnit->mDbgModule->mHotIdx; BF_ASSERT(vecIdx >= 0); while (vecIdx >= (int)mHotReplacedDbgLineInfo.size()) mHotReplacedDbgLineInfo.push_back(new HotReplacedLineInfo()); auto hotReplacedLineInfo = mHotReplacedDbgLineInfo[vecIdx]; HotReplacedLineInfo::Entry entry; entry.mSubprogram = dbgSubprogram; entry.mLineInfo = dbgSubprogram->mLineInfo; hotReplacedLineInfo->mEntries.Add(entry); } } void DbgSrcFile::RehupLineData() { for (int idx = 0; idx < (int)mLineDataRefs.size(); idx++) { auto dbgSubprogram = mLineDataRefs[idx]; if (dbgSubprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_None) { mLineDataRefs.RemoveAtFast(idx); idx--; } } } const String& DbgSrcFile::GetLocalPath() { return (!mLocalPath.IsEmpty()) ? mLocalPath : mFilePath; } void DbgSrcFile::GetHash(String& outStr) { if (mHashKind == DbgHashKind_MD5) { for (int i = 0; i < 16; i++) { outStr += StrFormat("%02X", mHash[i]); } } else if (mHashKind == DbgHashKind_SHA256) { for (int i = 0; i < 32; i++) { outStr += StrFormat("%02X", mHash[i]); } } } ////////////////////////////////////////////////////////////////////////// DbgType::DbgType() { mTypeIdx = -1; //mHash = 0; mIsDeclaration = false; mParent = NULL; //mName = NULL; mTypeName = NULL; mTypeCode = DbgType_Null; mSize = 0; //mArraySize = 0; mDeclLine = 0; mPtrType = NULL; mTypeParam = NULL; mBlockParam = NULL; mNext = NULL; mPriority = DbgTypePriority_Normal; } DbgType::~DbgType() { BfLogDbg("DbgType::~DWType %p\n", this); } DbgType* DbgType::ResolveTypeDef() { if (mTypeCode == DbgType_TypeDef) return mTypeParam->ResolveTypeDef(); return this; } bool DbgType::Equals(DbgType* dbgType) { if (dbgType == NULL) return false; if (mTypeCode != dbgType->mTypeCode) return false; if ((mName == NULL) != (dbgType->mName == NULL)) return false; if (mName != NULL) { if (dbgType->mFixedName) FixName(); else if (mFixedName) dbgType->FixName(); if (strcmp(mName, dbgType->mName) != 0) return false; } if ((mTypeParam != NULL) && (!mTypeParam->Equals(dbgType->mTypeParam))) return false; // Did mName already include the parent name? if (mCompileUnit->mDbgModule->mDbgFlavor == DbgFlavor_MS) return true; if ((mParent != NULL) != (dbgType->mParent != NULL)) return false; if (mParent != NULL) return mParent->Equals(dbgType->mParent); return true; } bool DbgType::IsStruct() { return mTypeCode == DbgType_Struct; } bool DbgType::IsPrimitiveType() { return (mTypeCode >= DbgType_i8) && (mTypeCode <= DbgType_Bool); } bool DbgType::IsNull() { return mTypeCode == DbgType_Null; } bool DbgType::IsVoid() { return (mTypeCode == DbgType_Void); } bool DbgType::IsValuelessType() { return ((mTypeCode == DbgType_Struct) && (GetByteCount() == 0)) || (mTypeCode == DbgType_Void); } bool DbgType::IsValueType() { return (mTypeCode <= DbgType_DefinitionEnd); } bool DbgType::IsTypedPrimitive() { if (mTypeCode != DbgType_Struct) return false; return mTypeParam != NULL; } bool DbgType::IsBoolean() { return mTypeCode == DbgType_Bool; } bool DbgType::IsInteger() { return (mTypeCode >= DbgType_i8) && (mTypeCode <= DbgType_u64); } bool DbgType::IsIntegral() { return ((mTypeCode >= DbgType_i8) && (mTypeCode <= DbgType_u64)) || ((mTypeCode >= DbgType_SChar) && (mTypeCode <= DbgType_UChar32)); } bool DbgType::IsChar() { return (mTypeCode >= DbgType_SChar) && (mTypeCode <= DbgType_UChar32); } bool DbgType::IsChar(DbgLanguage language) { if (language == DbgLanguage_Beef) return (mTypeCode >= DbgType_UChar) && (mTypeCode <= DbgType_UChar32); return (mTypeCode >= DbgType_SChar) && (mTypeCode <= DbgType_SChar32); } bool DbgType::IsFloat() { return (mTypeCode == DbgType_Single) || (mTypeCode == DbgType_Double); } // "Struct" in this sense means that we do NOT have a pointer to this value, but it may or may not be a Beef Struct bool DbgType::IsCompositeType() { if (((mTypeCode == DbgType_TypeDef) || (mTypeCode == DbgType_Const)) && (mTypeParam != NULL)) return mTypeParam->IsCompositeType(); return ((mTypeCode == DbgType_Struct) || (mTypeCode == DbgType_Class) || (mTypeCode == DbgType_SizedArray)); } bool DbgType::WantsRefThis() { return (GetLanguage() == DbgLanguage_Beef) && (!IsBfObject()); } bool DbgType::IsBfObjectPtr() { if ((mTypeCode == DbgType_Ptr) && (mTypeParam != NULL)) return mTypeParam->IsBfObject(); return false; } DbgExtType DbgType::CalcExtType() { auto language = GetLanguage(); if ((!mFixedName) && (language == DbgLanguage_Beef)) { FixName(); } auto primaryType = GetPrimaryType(); if (this != primaryType) { return primaryType->CalcExtType(); } if (mCompileUnit == NULL) return DbgExtType_Normal; if (language != DbgLanguage_Beef) return DbgExtType_Normal; if ((mTypeCode != DbgType_Struct) && (mTypeCode != DbgType_Class)) return DbgExtType_Normal; PopulateType(); if (mExtType != DbgExtType_Unknown) return mExtType; auto baseType = GetBaseType(); if (baseType == NULL) { if (mParent == NULL) return DbgExtType_Normal; if (mParent->mTypeCode != DbgType_Namespace) return DbgExtType_Normal; if (mParent->mParent != NULL) return DbgExtType_Normal; if (strcmp(mParent->mTypeName, "System") != 0) return DbgExtType_Normal; if (strcmp(mTypeName, "Object") != 0) return DbgExtType_Normal; return DbgExtType_BfObject; } else { if (strcmp(baseType->mTypeName, "Enum") == 0) { for (auto member : mMemberList) { if (strcmp(member->mName, "__bftag") == 0) return DbgExtType_BfPayloadEnum; } return DbgExtType_Normal; } else if (strcmp(baseType->mTypeName, "ValueType") == 0) { for (auto member : mMemberList) { if (strcmp(member->mName, "__bfunion") == 0) return DbgExtType_BfUnion; } } } auto baseExtType = baseType->CalcExtType(); if ((baseExtType == DbgExtType_BfObject) && (GetByteCount() == 0)) baseExtType = DbgExtType_Interface; return baseExtType; } DbgLanguage DbgType::GetLanguage() { return mLanguage; } void DbgType::FixName() { if (mFixedName) return; int depthCount = 0; auto dbgModule = mCompileUnit->mDbgModule; if ((dbgModule->mDbgFlavor == DbgFlavor_MS) && (mName != NULL) && (strlen(mName) > 0)) { bool modified = false; if (!dbgModule->DbgIsStrMutable(mName)) mName = dbgModule->DbgDupString(mName); const char* typeNamePtr = mTypeName; char* nameP = (char*)mName; // Fix the name char* inPtr = nameP; char* outPtr = nameP; while (true) { char c = *(inPtr++); if ((c == '<') || (c == '(')) depthCount++; else if ((c == '>') || (c == ')')) depthCount--; if ((c == ':') && (inPtr[0] == ':')) { modified = true; inPtr++; *(outPtr++) = '.'; if (depthCount == 0) typeNamePtr = outPtr; } else if (modified) *(outPtr++) = c; else outPtr++; if (c == 0) break; } if ((modified) && (mName != mTypeName) && (typeNamePtr != NULL)) { mTypeName = typeNamePtr; } } mFixedName = true; } bool DbgType::IsBfObject() { if (mExtType == DbgExtType_Unknown) mExtType = CalcExtType(); return (mExtType == DbgExtType_BfObject) || (mExtType == DbgExtType_Interface); } bool DbgType::IsBfPayloadEnum() { if (mExtType == DbgExtType_Unknown) mExtType = CalcExtType(); return mExtType == DbgExtType_BfPayloadEnum; } bool DbgType::IsBfUnion() { if (mExtType == DbgExtType_Unknown) mExtType = CalcExtType(); return mExtType == DbgExtType_BfUnion; } bool DbgType::IsBfEnum() { if (mTypeCode != DbgType_Struct) return false; auto baseType = GetBaseType(); if (baseType == NULL) { if (mParent == NULL) return false; if (mParent->mTypeCode != DbgType_Namespace) return false; if (mParent->mParent != NULL) return false; if (strcmp(mParent->mTypeName, "System") != 0) return false; return strcmp(mTypeName, "Enum") == 0; } return baseType->IsBfEnum(); } bool DbgType::IsBfTuple() { if (mTypeCode != DbgType_Struct) return false; if (GetLanguage() != DbgLanguage_Beef) return false; if (mName == NULL) return false; return mName[0] == '('; } bool DbgType::HasCPPVTable() { if ((mTypeCode != DbgType_Struct) && (mTypeCode != DbgType_Class)) return false; /*if (!mMemberList.IsEmpty()) { //TODO: We commented this out at some point- why did we do that? if ((mMemberList.mHead->mName != NULL) && (strncmp(mMemberList.mHead->mName, "_vptr$", 6) == 0)) return true; }*/ if (mHasVTable) return true; if (GetLanguage() == DbgLanguage_Beef) return false; for (auto checkBaseType : mBaseTypes) { if (checkBaseType->mBaseType->HasCPPVTable()) return true; } return false; } bool DbgType::IsBaseBfObject() { auto baseType = GetBaseType(); return (baseType == NULL) && (IsBfObject()); } bool DbgType::IsInterface() { if (mExtType == DbgExtType_Unknown) mExtType = CalcExtType(); return mExtType == DbgExtType_Interface; } bool DbgType::IsNamespace() { return mTypeCode == DbgType_Namespace; } bool DbgType::IsEnum() { return (mTypeCode == DbgType_Enum); } bool DbgType::IsRoot() { return (mTypeCode == DbgType_Root); } bool DbgType::IsRef() { return (mTypeCode == DbgType_Ref) || (mTypeCode == DbgType_RValueReference); } bool DbgType::IsSigned() { return (mTypeCode == DbgType_i8) || (mTypeCode == DbgType_i16) || (mTypeCode == DbgType_i32) || (mTypeCode == DbgType_i64); } bool DbgType::IsConst() { if ((mTypeCode == DbgType_Ptr) || (mTypeCode == DbgType_Ref)) { if (mTypeParam != NULL) return mTypeParam->IsConst(); } return mTypeCode == DbgType_Const; } bool DbgType::IsPointer(bool includeBfObjectPointer) { if (mTypeCode != DbgType_Ptr) return false; if ((!includeBfObjectPointer) && (mTypeParam != NULL) && (mTypeParam->IsBfObject())) return false; return true; } bool DbgType::HasPointer(bool includeBfObjectPointer) { if (((mTypeCode == DbgType_Const) || (mTypeCode == DbgType_Ref)) && (mTypeParam != NULL)) return mTypeParam->IsPointer(includeBfObjectPointer); return IsPointer(includeBfObjectPointer); } bool DbgType::IsPointerOrRef(bool includeBfObjectPointer) { if ((mTypeCode != DbgType_Ptr) && (mTypeCode != DbgType_Ref) && (mTypeCode != DbgType_RValueReference)) return false; if ((!includeBfObjectPointer) && (mTypeParam != NULL) && (mTypeParam->IsBfObject())) return false; return true; } bool DbgType::IsSizedArray() { return (mTypeCode == DbgType_SizedArray); } bool DbgType::IsAnonymous() { return (mTypeName == NULL) || (mTypeName[0] == '<'); } bool DbgType::IsGlobalsContainer() { return (mTypeName != NULL) && (mTypeName[0] == 'G') && (mTypeName[1] == '$'); } DbgType* DbgType::GetUnderlyingType() { return mTypeParam; } void DbgType::PopulateType() { if (mIsIncomplete) { mCompileUnit->mDbgModule->PopulateType(this); mIsIncomplete = false; } } DbgModule* DbgType::GetDbgModule() { if (mCompileUnit == NULL) return NULL; return mCompileUnit->mDbgModule; } DbgType* DbgType::GetPrimaryType() { if (mPrimaryType != NULL) return mPrimaryType; mPrimaryType = this; if (mPriority <= DbgTypePriority_Normal) { if ((mCompileUnit != NULL) && ((mCompileUnit->mLanguage == DbgLanguage_Beef) || (mTypeCode == DbgType_Namespace) || (mIsDeclaration))) { mPrimaryType = mCompileUnit->mDbgModule->GetPrimaryType(this); mPrimaryType->PopulateType(); mTypeCode = mPrimaryType->mTypeCode; mTypeParam = mPrimaryType->mTypeParam; } } return mPrimaryType; } DbgType* DbgType::GetBaseType() { auto primaryType = GetPrimaryType(); if (primaryType != this) return primaryType->GetBaseType(); PopulateType(); if (mBaseTypes.mHead == NULL) return NULL; if (GetLanguage() != DbgLanguage_Beef) return NULL; auto baseType = mBaseTypes.mHead->mBaseType; BF_ASSERT(!baseType->IsInterface()); if ((baseType == NULL) || (baseType->mPriority > DbgTypePriority_Normal)) return baseType; baseType = mCompileUnit->mDbgModule->GetPrimaryType(baseType); mBaseTypes.mHead->mBaseType = baseType; if (baseType->mIsDeclaration) { // That's no good, try to fix it up if (baseType->GetLanguage() == DbgLanguage_Beef) { if (baseType->GetBaseType() == NULL) { if (baseType->ToString() == "System.Function") { DbgBaseTypeEntry* baseTypeEntry = mCompileUnit->mDbgModule->mAlloc.Alloc(); baseTypeEntry->mBaseType = mCompileUnit->mDbgModule->GetPrimitiveType(DbgType_IntPtr_Alias, DbgLanguage_Beef); baseType->mBaseTypes.PushBack(baseTypeEntry); } } } } return baseType; } DbgType* DbgType::GetRootBaseType() { auto baseType = GetBaseType(); if (baseType != NULL) return baseType->GetRootBaseType(); return this; } DbgType* DbgType::RemoveModifiers(bool* hadRef) { DbgType* dbgType = this; while (dbgType != NULL) { bool curHadRef = (dbgType->mTypeCode == DbgType_Ref) || (dbgType->mTypeCode == DbgType_RValueReference); if ((curHadRef) && (hadRef != NULL)) *hadRef = true; if ((dbgType->mTypeCode == DbgType_Const) || (dbgType->mTypeCode == DbgType_TypeDef) || (dbgType->mTypeCode == DbgType_Volatile) || (dbgType->mTypeCode == DbgType_Bitfield) || (dbgType->mTypeCode == DbgType_Unaligned) || (curHadRef)) { if (dbgType->mTypeParam == NULL) break; dbgType = dbgType->mTypeParam; } else break; } return dbgType; } String DbgType::ToStringRaw(DbgLanguage language) { if (mTypeIdx != -1) return StrFormat("_T_%d", mTypeIdx); return ToString(language); } String DbgType::ToString(DbgLanguage language, bool allowDirectBfObject) { if (language == DbgLanguage_Unknown) language = GetLanguage(); if (language == DbgLanguage_Beef) { switch (mTypeCode) { case DbgType_UChar: return "char8"; case DbgType_UChar16: return "char16"; case DbgType_UChar32: return "char32"; case DbgType_i8: return "int8"; case DbgType_u8: return "uint8"; case DbgType_i16: return "int16"; case DbgType_u16: return "uint16"; case DbgType_i32: return "int32"; case DbgType_u32: return "uint32"; case DbgType_i64: return "int64"; case DbgType_u64: return "uint64"; } } else { switch (mTypeCode) { case DbgType_SChar: return "char"; case DbgType_SChar16: return "wchar_t"; case DbgType_SChar32: return "int32_t"; case DbgType_UChar: return "uint8_t"; case DbgType_UChar16: return "uint16_t"; case DbgType_UChar32: return "uint32_t"; case DbgType_i8: return "char"; case DbgType_u8: return "uint8_t"; case DbgType_i16: return "short"; case DbgType_u16: return "uint16_t"; case DbgType_i32: return "int"; case DbgType_u32: return "uint32_t"; case DbgType_i64: return "int64_t"; case DbgType_u64: return "uint64_t"; } } if (mTypeName != NULL) { if ((!allowDirectBfObject) && (IsBfObject())) { // Only use the '#' for testing //return ToString(true) + "#"; return ToString(DbgLanguage_Unknown, true); } if (IsGlobalsContainer()) { if (mParent != NULL) return mParent->ToString(language); return ""; } char* nameP = (char*)mTypeName; //String combName; /*if (mTemplateParams != NULL) { combName = nameP; combName += mTemplateParams; nameP = combName.c_str(); }*/ if ((!mFixedName) && (language == DbgLanguage_Beef)) { FixName(); } if (mParent == NULL) { if (strncmp(nameP, "Box<", 4) == 0) return String(nameP + 4, nameP + strlen(nameP) - 1) + "^"; // For declarations, may also include namespaces return mName; } if (GetLanguage() == DbgLanguage_Beef) return mParent->ToString(language) + "." + nameP; else return mParent->ToString(language) + "::" + nameP; } switch (mTypeCode) { case DbgType_Struct: { if ((mTypeName == NULL) && (mParent != NULL)) return mParent->ToString(language); return "@struct"; } case DbgType_Class: { return "@class"; } case DbgType_TypeDef: { return "@typedef"; } case DbgType_Const: { if (language == DbgLanguage_Beef) { if (mTypeParam == NULL) return "readonly"; return "readonly " + mTypeParam->ToString(language); } if (mTypeParam == NULL) return "const"; return "const " + mTypeParam->ToString(language); } case DbgType_Volatile: { if (mTypeParam == NULL) return "volatile"; return "volatile " + mTypeParam->ToString(language); } case DbgType_Unaligned: { if (mTypeParam == NULL) return "unaligned"; return "unaligned " + mTypeParam->ToString(language); } case DbgType_Restrict: { if (mTypeParam == NULL) return "restrict"; return "restrict " + mTypeParam->ToString(language); } case DbgType_Ptr: { if (mTypeParam == NULL) return "void*"; if (mTypeParam->IsBfObject()) return mTypeParam->ToString(DbgLanguage_Unknown, true); // Don't put a "*" on the end of a function type, it's implicit if (mTypeParam->mTypeCode == DbgType_Subroutine) return mTypeParam->ToString(language); return mTypeParam->ToString(language) + "*"; } case DbgType_Ref: { if (language == DbgLanguage_Beef) { if (mTypeParam == NULL) return "ref"; return "ref " + mTypeParam->ToString(language); } if (mTypeParam == NULL) return "&"; return mTypeParam->ToString(language) + "&"; } case DbgType_RValueReference: { if (language == DbgLanguage_Beef) { // Ignore this - this is used for passing structs when we're not using the 'byval' attribute return mTypeParam->ToString(language); } if (mTypeParam == NULL) return "&&"; return mTypeParam->ToString(language) + "&&"; } case DbgType_Unspecified: return mTypeName; case DbgType_SizedArray: { String name; auto checkType = this; while (checkType->mTypeCode == DbgType_SizedArray) { intptr innerSize = checkType->mTypeParam->GetStride(); intptr arrSize = 0; if (innerSize > 0) { arrSize = checkType->GetStride() / innerSize; } name += StrFormat("[%lld]", arrSize); checkType = checkType->mTypeParam; } name = checkType->ToString(language) + name; return name; } case DbgType_Union: { if (mTypeName != NULL) return String("union ") + mTypeName; return "union"; } case DbgType_Single: return "float"; case DbgType_Double: return "double"; case DbgType_Null: return "void"; case DbgType_Subroutine: { String str; str += mTypeParam->ToString(language); str += " ("; int paramIdx = 0; for (auto param : mBlockParam->mVariables) { if (paramIdx > 0) str += ", "; str += param->mType->ToString(language); paramIdx++; } str += ")"; return str; } case DbgType_VTable: return "@vtable"; case DbgType_Enum: return "@enum"; case DbgType_Namespace: { // Anonymous return "`anon`"; } case DbgType_PtrToMember: return "@ptrToMember"; case DbgType_Bitfield: { auto dbgBitfieldType = (DbgBitfieldType*)this; return mTypeParam->ToString(language) + StrFormat("{%d:%d}", dbgBitfieldType->mPosition, dbgBitfieldType->mLength); } default: break; } BF_FATAL("Unhandled type"); return "???"; } intptr DbgType::GetByteCount() { if (!mSizeCalculated) { PopulateType(); if ((mSize == 0) && (GetLanguage() == DbgLanguage_Beef)) CalcExtType(); if ((mTypeCode == DbgType_Struct) || (mTypeCode == DbgType_Class) || (mTypeCode == DbgType_Union)) { if (mPriority <= DbgTypePriority_Normal) { auto primaryType = GetPrimaryType(); if (primaryType != this) { mSize = primaryType->GetByteCount(); mAlign = primaryType->mAlign; } } } else if ((mTypeCode == DbgType_Ref) || (mTypeCode == DbgType_Ptr) || (mTypeCode == DbgType_PtrToMember)) { #ifdef BF_DBG_32 mSize = 4; #else mSize = 8; #endif } else if (mTypeCode == DbgType_SizedArray) { auto language = GetLanguage(); if (language == DbgLanguage_Beef) { if (mTypeParam->mAlign == 0) { NOP; } auto primaryType = mTypeParam->GetPrimaryType(); if (primaryType->mAlign == 0) { NOP; } else { intptr elemCount = BF_ALIGN(mSize, primaryType->mAlign) / primaryType->GetStride(); if (elemCount > 0) { mSize = ((elemCount - 1) * primaryType->GetStride()) + primaryType->GetByteCount(); } } mAlign = primaryType->mAlign; } } else if (mTypeParam != NULL) // typedef, const, volatile, restrict, etc mSize = mTypeParam->GetByteCount(); mSizeCalculated = true; } return mSize; } intptr DbgType::GetStride() { return BF_ALIGN(GetByteCount(), GetAlign()); } int DbgType::GetAlign() { if (mAlign == 0) { auto primaryType = GetPrimaryType(); if (primaryType != this) return primaryType->GetAlign(); if (IsCompositeType()) { PopulateType(); } } if (mAlign != 0) return mAlign; return 1; } void DbgType::EnsureMethodsMapped() { for (auto methodNameEntry : mMethodNameList) { if (methodNameEntry->mCompileUnitId != -1) { mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId); methodNameEntry->mCompileUnitId = -1; } } } #define CREATE_PRIMITIVE_C(typeCode, cTypeName, type) \ dbgType = mAlloc.Alloc(); \ dbgType->mCompileUnit = &mDefaultCompileUnit; \ dbgType->mName = cTypeName; \ dbgType->mLanguage = DbgLanguage_C;\ dbgType->mTypeName = cTypeName; \ dbgType->mTypeCode = typeCode; \ dbgType->mSize = sizeof(type); \ dbgType->mAlign = sizeof(type); \ mCPrimitiveTypes[typeCode] = dbgType; \ mTypeMap.Insert(dbgType); #define CREATE_PRIMITIVE(typeCode, cTypeName, bfTypeName, structName, type) \ dbgType = mAlloc.Alloc(); \ dbgType->mCompileUnit = &mDefaultCompileUnit; \ dbgType->mName = cTypeName; \ dbgType->mLanguage = DbgLanguage_C;\ dbgType->mTypeName = cTypeName; \ dbgType->mTypeCode = typeCode; \ dbgType->mSize = sizeof(type); \ dbgType->mAlign = sizeof(type); \ mCPrimitiveTypes[typeCode] = dbgType; \ mTypeMap.Insert(dbgType); \ dbgType = mAlloc.Alloc(); \ dbgType->mCompileUnit = &mDefaultCompileUnit; \ dbgType->mName = bfTypeName; \ dbgType->mLanguage = DbgLanguage_Beef;\ dbgType->mTypeName = bfTypeName; \ dbgType->mTypeCode = typeCode; \ dbgType->mSize = sizeof(type); \ dbgType->mAlign = sizeof(type); \ mBfPrimitiveTypes[typeCode] = dbgType; \ mPrimitiveStructNames[typeCode] = structName; \ mTypeMap.Insert(dbgType); DbgModule::DbgModule(DebugTarget* debugTarget) : mDefaultCompileUnit(this) { mMemReporter = NULL; mLoadState = DbgModuleLoadState_NotLoaded; mMappedImageFile = NULL; mEntryPoint = 0; mFailMsgPtr = NULL; mFailed = false; for (int i = 0; i < DbgType_COUNT; i++) { mBfPrimitiveTypes[i] = NULL; mCPrimitiveTypes[i] = NULL; mPrimitiveStructNames[i] = NULL; } DbgType* dbgType; mDefaultCompileUnit.mLanguage = DbgLanguage_Beef; mDefaultCompileUnit.mDbgModule = this; if (debugTarget != NULL) { // These are 'alias' definitions for C, but get overwritten by their official // stdint.h versions (ie: int8_t) CREATE_PRIMITIVE_C(DbgType_i8, "int8", int8); CREATE_PRIMITIVE_C(DbgType_i16, "int16", int16); CREATE_PRIMITIVE_C(DbgType_i32, "int32", int32); CREATE_PRIMITIVE_C(DbgType_i64, "int64", int64); CREATE_PRIMITIVE_C(DbgType_i8, "uint8", uint8); CREATE_PRIMITIVE_C(DbgType_i16, "uint16", uint16); CREATE_PRIMITIVE_C(DbgType_i32, "uint32", uint32); CREATE_PRIMITIVE_C(DbgType_i64, "uint64", uint64); CREATE_PRIMITIVE(DbgType_Void, "void", "void", "void", void*); dbgType->mSize = 0; dbgType->mAlign = 0; CREATE_PRIMITIVE(DbgType_Null, "null", "null", "null", void*); CREATE_PRIMITIVE(DbgType_IntPtr_Alias, "intptr_t", "int", "System.Int", intptr_target); CREATE_PRIMITIVE(DbgType_UIntPtr_Alias, "uintptr_t", "uint", "System.UInt", addr_target); CREATE_PRIMITIVE(DbgType_SChar, "char", "char", "System.Char", char); CREATE_PRIMITIVE(DbgType_SChar16, "wchar_t", "wchar", "System.Char16", wchar_t); CREATE_PRIMITIVE(DbgType_i8, "int8_t", "int8", "System.SByte", int8); CREATE_PRIMITIVE(DbgType_i16, "short", "int16", "System.Int16", int16); CREATE_PRIMITIVE(DbgType_i32, "int", "int32", "System.Int32", int32); CREATE_PRIMITIVE(DbgType_i64, "int64_t", "int64", "System.Int64", int64); CREATE_PRIMITIVE(DbgType_u8, "uint8_t", "uint8", "System.UInt8", uint8); CREATE_PRIMITIVE(DbgType_u16, "uint16_t", "uint16", "System.UInt16", uint16); CREATE_PRIMITIVE(DbgType_u32, "uint32_t", "uint32", "System.UInt32", uint32); CREATE_PRIMITIVE(DbgType_u64, "uint64_t", "uint64", "System.UInt64", uint64); CREATE_PRIMITIVE(DbgType_Single, "float", "float", "System.Single", float); CREATE_PRIMITIVE(DbgType_Double, "double", "double", "System.Double", double); CREATE_PRIMITIVE(DbgType_UChar, "char8", "char8", "System.Char", char); CREATE_PRIMITIVE(DbgType_UChar16, "char16", "char16", "System.Char16", short); CREATE_PRIMITIVE(DbgType_UChar32, "char32", "char32", "System.Char32", int); CREATE_PRIMITIVE(DbgType_Bool, "bool", "bool", "System.Boolean", bool); CREATE_PRIMITIVE(DbgType_Subroutine, "@Func", "@Func", "@Func", bool); CREATE_PRIMITIVE(DbgType_RawText, "@RawText", "@RawText", "@RawText", bool); CREATE_PRIMITIVE(DbgType_RegGroup, "@RegGroup", "@RegGroup", "@RegGroup", void*); CREATE_PRIMITIVE_C(DbgType_i8, "int16_t", int16_t); CREATE_PRIMITIVE_C(DbgType_i8, "int32_t", int32_t); CREATE_PRIMITIVE_C(DbgType_i64, "__int64", int64); CREATE_PRIMITIVE_C(DbgType_u64, "unsigned __int64", uint64); CREATE_PRIMITIVE_C(DbgType_u8, "unsigned char", uint8); CREATE_PRIMITIVE_C(DbgType_u16, "unsigned short", uint16); CREATE_PRIMITIVE_C(DbgType_u32, "unsigned int", uint32); CREATE_PRIMITIVE_C(DbgType_u32, "unsigned int32_t", uint32_t); CREATE_PRIMITIVE_C(DbgType_u32, "unsigned long", uint32); CREATE_PRIMITIVE_C(DbgType_u64, "unsigned int64_t", uint64); } mIsDwarf64 = false; mDebugTarget = debugTarget; if (debugTarget != NULL) mDebugger = debugTarget->mDebugger; else mDebugger = NULL; mDebugLineData = NULL; mDebugInfoData = NULL; mDebugPubNames = NULL; mDebugFrameAddress = 0; mDebugFrameData = NULL; mDebugLocationData = NULL; mDebugRangesData = NULL; mDebugAbbrevData = NULL; mDebugStrData = NULL; mDebugAbbrevPtrData = NULL; mEHFrameData = NULL; mEHFrameAddress = 0; mStringTable = NULL; mSymbolData = NULL; mCheckedBfObject = false; mBfObjectHasFlags = false; mModuleKind = DbgModuleKind_Module; mStartTypeIdx = 0; mEndTypeIdx = 0; mHotIdx = 0; mStartSubprogramIdx = 0; mEndSubprogramIdx = 0; mCodeAddress = NULL; mMayBeOld = false; mTimeStamp = 0; mExpectedFileSize = 0; mImageBase = 0; mPreferredImageBase = 0; mImageSize = 0; mOrigImageData = NULL; mDeleting = false; mAllocSizeData = 0; mParsedSymbolData = false; mParsedTypeData = false; mParsedGlobalsData = false; mPopulatedStaticVariables = false; mParsedFrameDescriptors = false; mTLSAddr = 0; mTLSSize = 0; mTLSExtraAddr = 0; mTLSExtraSize = 0; mTLSIndexAddr = 0; mDbgFlavor = DbgFlavor_Unknown; mMasterCompileUnit = NULL; } DbgModule::~DbgModule() { delete mMemReporter; for (auto dwSrcFile : mEmptySrcFiles) delete dwSrcFile; for (auto dwCompileUnit : mCompileUnits) delete dwCompileUnit; delete mSymbolData; delete mStringTable; delete mDebugLineData; delete mDebugInfoData; delete mDebugPubNames; delete mDebugFrameData; delete mDebugLocationData; delete mDebugRangesData; delete mDebugAbbrevData; delete mDebugAbbrevPtrData; delete mDebugStrData; for (auto entry : mExceptionDirectory) delete entry.mData; delete mEHFrameData; delete mOrigImageData; if ((IsObjectFile()) && (mImageBase != 0)) { mDebugger->ReleaseHotTargetMemory((addr_target)mImageBase, (int)mImageSize); } for (auto data : mOwnedSectionData) delete data; } DbgSubprogram* DbgModule::FindSubprogram(DbgType* dbgType, const char * methodName) { dbgType = dbgType->GetPrimaryType(); dbgType->PopulateType(); if (dbgType->mNeedsGlobalsPopulated) PopulateTypeGlobals(dbgType); for (auto methodNameEntry : dbgType->mMethodNameList) { if ((methodNameEntry->mCompileUnitId != -1) && (strcmp(methodNameEntry->mName, methodName) == 0)) { // If we hot-replaced this type then we replaced and parsed all the methods too if (!dbgType->mCompileUnit->mDbgModule->IsObjectFile()) dbgType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId); methodNameEntry->mCompileUnitId = -1; } } DbgSubprogram* result = NULL; for (auto method : dbgType->mMethodList) { if (strcmp(method->mName, methodName) == 0) { method->PopulateSubprogram(); if ((result == NULL) || (method->mBlock.mLowPC != 0)) result = method; } } return result; } void DbgModule::Fail(const StringImpl& error) { if (mFailMsgPtr != NULL) { if (mFailMsgPtr->IsEmpty()) *mFailMsgPtr = error; } mDebugger->OutputRawMessage("error " + error + "\n"); mFailed = true; } char* DbgModule::DbgDupString(const char* str, const char* allocName) { int strLen = (int)strlen(str); if (strLen == 0) return NULL; char* dupStr = (char*)mAlloc.AllocBytes(strLen + 1, (allocName != NULL) ? allocName : "DbgDupString"); memcpy(dupStr, str, strLen); return dupStr; } DbgModule* DbgModule::GetLinkedModule() { if (IsObjectFile()) return mDebugTarget->mTargetBinary; return this; } addr_target DbgModule::GetTargetImageBase() { if (IsObjectFile()) return (addr_target)mDebugTarget->mTargetBinary->mImageBase; return (addr_target)mImageBase; } void DbgModule::ParseGlobalsData() { mParsedGlobalsData = true; } void DbgModule::ParseSymbolData() { mParsedSymbolData = true; } void DbgModule::ParseTypeData() { mParsedTypeData = true; } DbgCompileUnit* DbgModule::ParseCompileUnit(int compileUnitId) { return NULL; } void DbgModule::MapCompileUnitMethods(DbgCompileUnit * compileUnit) { } void DbgModule::MapCompileUnitMethods(int compileUnitId) { } void DbgModule::PopulateType(DbgType* dbgType) { } void DbgModule::PopulateTypeGlobals(DbgType* dbgType) { } void DbgModule::PopulateStaticVariableMap() { if (mPopulatedStaticVariables) return; for (auto staticVariable : mStaticVariables) { mStaticVariableMap[staticVariable->GetMappedName()] = staticVariable; } mPopulatedStaticVariables = true; } void DbgModule::ProcessDebugInfo() { } addr_target DbgModule::RemapAddr(addr_target addr) { if ((addr != 0) && (mPreferredImageBase != 0) && (mImageBase != 0)) return addr + (intptr_target)(mImageBase - mPreferredImageBase); return addr; } void DbgModule::ParseAbbrevData(const uint8* data) { while (true) { int abbrevIdx = (int)DecodeULEB128(data); mDebugAbbrevPtrData[abbrevIdx] = data; if (abbrevIdx == 0) break; int entryTag = (int)DecodeULEB128(data); bool hasChildren = GET(char) == DW_CHILDREN_yes; while (true) { int attrName = (int)DecodeULEB128(data); int form = (int)DecodeULEB128(data); if ((attrName == 0) && (form == 0)) break; } } } void DbgModule::ParseExceptionData() { if (mExceptionDirectory.IsEmpty()) return; BP_ZONE("DbgModule::ParseExceptionData"); for (auto entry : mExceptionDirectory) { const uint8* data = entry.mData; const uint8* dataEnd = data + entry.mSize; static int entryCount = 0; addr_target imageBase = GetTargetImageBase(); while (data < dataEnd) { addr_target beginAddress = GET(uint32); addr_target endAddress = GET(uint32); uint32 unwindData = GET(uint32); //TODO: Apparently unwindData can refer to another runtime entry in the .pdata if the LSB is set to 1? beginAddress += (addr_target)imageBase; endAddress += (addr_target)imageBase; int exSize = (int)(endAddress - beginAddress); for (int exOffset = 0; true; exOffset += DBG_MAX_LOOKBACK) { int curSize = exSize - exOffset; if (curSize <= 0) break; BP_ALLOC_T(DbgExceptionDirectoryEntry); DbgExceptionDirectoryEntry* exceptionDirectoryEntry = mAlloc.Alloc(); exceptionDirectoryEntry->mAddress = beginAddress + exOffset; exceptionDirectoryEntry->mOrigAddressOffset = exOffset; exceptionDirectoryEntry->mAddressLength = curSize; exceptionDirectoryEntry->mExceptionPos = (int)unwindData; exceptionDirectoryEntry->mDbgModule = this; mDebugTarget->mExceptionDirectoryMap.Insert(exceptionDirectoryEntry); entryCount++; } } } } static int gIdx = 0; template static bool IsTypeSigned() { return false; } template <> bool IsTypeSigned() { return true; } template <> bool IsTypeSigned() { return true; } template <> bool IsTypeSigned() { return true; } template <> bool IsTypeSigned() { return true; } #pragma warning(push) #pragma warning(disable:4302) #pragma warning(disable:4311) #pragma warning(disable:4312) #pragma warning(disable:4800) #pragma warning(disable:4800) template T DbgModule::ReadValue(const uint8*& data, int form, int refOffset, const uint8** extraData, const uint8* startData) { gIdx++; switch (form) { case DW_FORM_strp: { int strOffset = GET(int); BF_ASSERT(mDebugStrData != NULL); const char* str = (const char*)mDebugStrData + strOffset; return (T)(intptr)str; } break; case DW_FORM_data1: { if (IsTypeSigned()) return (T)(intptr)GET(int8); else return (T)(uintptr)GET(uint8); } break; case DW_FORM_data2: { if (IsTypeSigned()) return (T)(intptr)GET(int16); else return (T)(uintptr)GET(uint16); } break; case DW_FORM_data4: { if (IsTypeSigned()) return (T)(intptr)GET(int32); else return (T)(uintptr)GET(uint32); } break; case DW_FORM_data8: { if (IsTypeSigned()) return (T)GET(int64); else return (T)GET(uint64); } break; case DW_FORM_ref1: { return (T)(intptr)GET(int8) + refOffset; } break; case DW_FORM_ref2: { return (T)(intptr)GET(int16) + refOffset; } break; case DW_FORM_ref4: { return (T)(intptr)GET(int32) + refOffset; } break; case DW_FORM_sec_offset: { intptr_target offset; if (mIsDwarf64) offset = (intptr_target)GET(int64); else offset = GET(int32); if (extraData != NULL) { *extraData = mDebugLocationData + offset; return 0; } return (T)offset; } break; case DW_FORM_addr: { return (T)GET(addr_target); } break; case DW_FORM_exprloc: { int64_t exprLen = DecodeULEB128(data); const uint8* endData = data + exprLen; if (extraData != NULL) *extraData = data; data = endData; return (T)exprLen; } break; case DW_FORM_flag_present: { // return (T)1; } break; case DW_FORM_flag: { // return (T)(intptr)GET(char); } break; case DW_FORM_sdata: return (T)DecodeSLEB128(data); case DW_FORM_udata: return (T)DecodeULEB128(data); case DW_FORM_string: { const char* str = (const char*)data; while (true) { uint8 val = *data; data++; if (val == 0) return (T)(intptr)str; } } case DW_FORM_block: { int blockLen = (int)DecodeULEB128(data); const uint8* retVal = data; data += blockLen; return (T)(intptr)retVal; } case DW_FORM_block1: { int blockLen = (int)*((uint8*)data); data += sizeof(uint8); const uint8* retVal = data; data += blockLen; return (T)(intptr)retVal; } default: assert("Not covered!" == 0); break; } return (T)0; } #pragma warning(pop) static int gAbbrevNum = 0; DbgType* DbgModule::GetOrCreateType(int typeIdx, DbgDataMap& dataMap) { if (typeIdx == 0) return NULL; DbgModule* linkedModule = GetLinkedModule(); DbgType* dbgType = dataMap.Get(typeIdx); if (dbgType != NULL) return dbgType; dbgType = mAlloc.Alloc(); dbgType->mTypeIdx = (int)linkedModule->mTypes.size(); linkedModule->mTypes.push_back(dbgType); dataMap.Set(typeIdx, dbgType); return dbgType; } typedef std::pair DataPair; typedef llvm::SmallVector DataStack; template T DbgModule::GetOrCreate(int idx, DbgDataMap& dataMap) { if (idx == 0) return NULL; T val = dataMap.Get(idx); if (val != NULL) return val; val = mAlloc.Alloc::type >(); dataMap.Set(idx, val); return val; } template static T GetStackTop(DataStack* dataStack) { auto dataPair = dataStack->back(); if (dataPair.first == RemoveTypePointer::type::ClassType) return (T)dataPair.second; return NULL; } template <> DbgBlock* GetStackTop(DataStack* dataStack) { auto dataPair = dataStack->back(); if (dataPair.first == DbgBlock::ClassType) return (DbgBlock*)dataPair.second; if (dataPair.first == DbgSubprogram::ClassType) return &((DbgSubprogram*)dataPair.second)->mBlock; if (dataPair.first == DbgType::ClassType) return ((DbgType*)dataPair.second)->mBlockParam; return NULL; } template static bool StackHasType(DataStack* dataStack) { for (auto itr : *dataStack) if (itr.first == RemoveTypePointer::type::ClassType) return true; return false; } template static T GetStackLast(DataStack* dataStack) { for (int i = (int)dataStack->size() - 1; i >= 0; i--) { if ((*dataStack)[i].first == RemoveTypePointer::type::ClassType) return (T)(*dataStack)[i].second; } return NULL; } template static DataPair MakeDataPair(T* data) { return DataPair(T::ClassType, data); } void DbgModule::FixupInnerTypes(int startingTypeIdx) { BP_ZONE("DbgModule_FixupInnerTypes"); for (int typeIdx = startingTypeIdx; typeIdx < (int)mTypes.size(); typeIdx++) { DbgType* dbgType = mTypes[typeIdx]; if ((dbgType->mPriority == DbgTypePriority_Primary_Implicit) && (dbgType->mParent != NULL) && (dbgType->mParent->mTypeCode != DbgType_Namespace) && (dbgType->mParent->mPriority <= DbgTypePriority_Primary_Implicit)) { auto primaryParent = dbgType->mParent->GetPrimaryType(); dbgType->mParent->mSubTypeList.Clear(); dbgType->mParent = primaryParent; primaryParent->mSubTypeList.PushBack(dbgType); } } } void DbgModule::MapTypes(int startingTypeIdx) { BP_ZONE("DbgModule_MapTypes"); bool needsInnerFixups = false; for (int typeIdx = startingTypeIdx; typeIdx < (int)mTypes.size(); typeIdx++) { DbgType* dbgType = mTypes[typeIdx]; BF_ASSERT(dbgType->mTypeCode != DbgType_Null); if ((dbgType->mTypeCode == DbgType_Namespace) && (dbgType->mPriority < DbgTypePriority_Primary_Implicit)) continue; //TODO: Always valid? if (dbgType->mIsDeclaration) continue; // We were avoiding adding '<' names before, but that made it impossible to look up auto-named primary types , // like in-place unions like '' if ((dbgType->mTypeName == NULL) || (dbgType->mName == NULL) /*|| (dbgType->mTypeName[0] == '<')*/) continue; if (dbgType->mTypeCode > DbgType_DefinitionEnd) { // Only add "definition types" continue; } if (dbgType->mTypeCode == DbgType_Namespace) { bool isQualifiedNamespace = false; for (const char* cPtr = dbgType->mTypeName; *cPtr != '\0'; cPtr++) if (*cPtr == '.') isQualifiedNamespace = true; if (isQualifiedNamespace) continue; // Don't add fully qualified namespaces (they come from the 'using' implementation)* } if (dbgType->mHasStaticMembers) { for (auto member : dbgType->mMemberList) if ((member->mIsStatic) && (member->mLocationData != NULL)) dbgType->mDefinedMembersCount++; } if ((dbgType->mTypeName != NULL) && (strcmp(dbgType->mTypeName, "@") == 0)) { // Globals type. continue; } auto prevTypeEntry = FindType(dbgType->mName, dbgType->mLanguage); // Only replace previous instance if its a declaration if (prevTypeEntry != NULL) { auto prevType = prevTypeEntry->mValue; if (dbgType->mCompileUnit->mDbgModule != prevType->mCompileUnit->mDbgModule) { // Don't replace original types with hot types -- those need to be inserted in the the hot alternates list BF_ASSERT(dbgType->mCompileUnit->mDbgModule->IsObjectFile()); prevType->mHotNewType = dbgType; continue; } // Never override explicit primaries if (prevType->mPriority == DbgTypePriority_Primary_Explicit) continue; if (dbgType->mTypeCode == DbgType_TypeDef) { // Typedef can never override anything continue; } if (prevType->mTypeCode == DbgType_TypeDef) { if (dbgType->mTypeCode != DbgType_TypeDef) { // Allow this to override prevTypeEntry->mValue = dbgType; } continue; } // Don't replace a ptr to an BfObject with a BfObject if ((prevType->mTypeCode == DbgType_Ptr) && (dbgType->mTypeCode == DbgType_Struct)) continue; if ((prevType->mTypeCode == DbgType_Struct) && (dbgType->mTypeCode == DbgType_Ptr)) { // Allow this to override prevTypeEntry->mValue = dbgType; continue; } if (prevType->mTypeCode == DbgType_Namespace) { if (dbgType->mTypeCode != DbgType_Namespace) { // Old type was namespace but new isn't? Replace old type. while (!prevType->mSubTypeList.IsEmpty()) { DbgType* subType = prevType->mSubTypeList.PopFront(); subType->mParent = dbgType; dbgType->mSubTypeList.PushBack(subType); } prevType->mPriority = DbgTypePriority_Normal; if (dbgType->mPriority < DbgTypePriority_Primary_Implicit) dbgType->mPriority = DbgTypePriority_Primary_Implicit; prevTypeEntry->mValue = dbgType; continue; } // We definitely didn't want to do this for MS. For DWARF? //prevType->mAlternates.PushFront(dbgType, &mAlloc); continue; } else { // New type is namespace but old wasn't? Ignore new type. if (dbgType->mTypeCode == DbgType_Namespace) continue; if (dbgType->mIsDeclaration) continue; if (!prevType->mIsDeclaration) { if ((prevType->mCompileUnit == NULL) || (dbgType->mLanguage < prevType->mLanguage)) { // We always want 'Beef' types to supersede 'C' types, but don't override the built-in primitive types continue; } if (prevType->mDefinedMembersCount > 0) { if (dbgType->mDefinedMembersCount > 0) { // We create an 'alternates' list for all types that define at least one static field prevType->mAlternates.PushFront(dbgType, &mAlloc); } continue; } if (prevType->mMethodsWithParamsCount > dbgType->mMethodsWithParamsCount) { // This handles a special case where methods without line data like .HasFlags doesn't show containing // params in cases where it gets inlined continue; } // Types with method lists are preferred if ((!prevType->mMethodList.IsEmpty()) && (dbgType->mMethodList.IsEmpty())) continue; if ((prevType->mTypeCode == DbgType_Ptr) && (prevType->mTypeParam != NULL) && (!prevType->mTypeParam->mMethodList.IsEmpty())) continue; } // Replace type if (!prevType->mSubTypeList.IsEmpty()) needsInnerFixups = true; prevType->mPriority = DbgTypePriority_Normal; if (dbgType->mPriority == DbgTypePriority_Normal) dbgType->mPriority = DbgTypePriority_Primary_Implicit; prevTypeEntry->mValue = dbgType; continue; } } if ((dbgType->mParent != NULL) && (dbgType->mParent->mTypeCode != DbgType_Namespace) && (dbgType->mParent->mPriority <= DbgTypePriority_Primary_Implicit)) needsInnerFixups = true; if (dbgType->mPriority == DbgTypePriority_Normal) dbgType->mPriority = DbgTypePriority_Primary_Implicit; mTypeMap.Insert(dbgType); } if (needsInnerFixups) FixupInnerTypes(startingTypeIdx); } void DbgModule::CreateNamespaces() { BP_ZONE("DbgModule::CreateNamespaces"); int startLength = (int)mTypes.size(); for (int typeIdx = 0; typeIdx < startLength; typeIdx++) { DbgType* dbgType = mTypes[typeIdx]; if (dbgType->mName == NULL) continue; if ((dbgType->mTypeCode == DbgType_Namespace) && (dbgType->mTagIdx != 0)) { auto namespaceTypeEntry = FindType(dbgType->mName, dbgType->GetLanguage()); DbgType* namespaceType; if (namespaceTypeEntry == NULL) { namespaceType = mAlloc.Alloc(); namespaceType->mTypeCode = DbgType_Namespace; namespaceType->mLanguage = dbgType->mLanguage; namespaceType->mCompileUnit = dbgType->mCompileUnit; namespaceType->mTypeIdx = (int)mTypes.size(); namespaceType->mPriority = DbgTypePriority_Primary_Explicit; namespaceType->mName = dbgType->mName; namespaceType->mTypeName = dbgType->mTypeName; if (dbgType->mParent != NULL) { namespaceType->mParent = dbgType->mParent->GetPrimaryType(); namespaceType->mParent->mSubTypeList.PushBack(namespaceType); } else { namespaceType->mCompileUnit->mGlobalType->mSubTypeList.PushBack(namespaceType); } mTypes.push_back(namespaceType); mTypeMap.Insert(namespaceType); } else namespaceType = namespaceTypeEntry->mValue; while (!dbgType->mMemberList.IsEmpty()) { DbgVariable* curVar = dbgType->mMemberList.PopFront(); namespaceType->mMemberList.PushBack(curVar); } DbgType* prevType = NULL; DbgType* curType = dbgType->mSubTypeList.mHead; while (curType != NULL) { DbgType* nextType = curType->mNext; if (curType->mPriority >= DbgTypePriority_Primary_Implicit) { dbgType->mSubTypeList.Remove(curType, prevType); namespaceType->mSubTypeList.PushBack(curType); } prevType = curType; curType = nextType; } continue; } } // If we didn't have a parent type for a namespace (ie: if System.Collections wasn't linked to System) then we wait // until the end and move those from the global list to the parent list for (int typeIdx = startLength; typeIdx < (int)mTypes.size(); typeIdx++) { DbgType* dbgType = mTypes[typeIdx]; if (dbgType->mParent != NULL) continue; char* typeName = (char*)dbgType->mTypeName; int lastDotIdx = -1; for (int i = 0; true; i++) { char c = typeName[i]; if (c == 0) break; if (c == '.') lastDotIdx = i; } if (lastDotIdx == -1) continue; typeName[lastDotIdx] = 0; dbgType->mTypeName = typeName + lastDotIdx + 1; auto parentEntry = FindType(typeName, dbgType->GetLanguage()); typeName[lastDotIdx] = '.'; if (parentEntry == NULL) continue; auto parentType = parentEntry->mValue; dbgType->mCompileUnit->mGlobalType->mSubTypeList.Remove(dbgType); dbgType->mParent = parentType; parentType->mSubTypeList.PushBack(dbgType); } } void DbgModule::FindTemplateStr(const char*& name, int& templateNameIdx) { if (templateNameIdx == 0) { for (int i = 0; name[i] != 0; i++) { if (name[i] == '<') { templateNameIdx = i; return; } } templateNameIdx = -1; } } void DbgModule::TempRemoveTemplateStr(const char*& name, int& templateNameIdx) { if (templateNameIdx == 0) FindTemplateStr(name, templateNameIdx); if (templateNameIdx == -1) return; if (!DbgIsStrMutable(name)) name = DbgDupString(name); ((char*)name)[templateNameIdx] = 0; } void DbgModule::ReplaceTemplateStr(const char*& name, int& templateNameIdx) { if (templateNameIdx > 0) ((char*)name)[templateNameIdx] = '<'; } void DbgModule::MapSubprogram(DbgSubprogram* dbgSubprogram) { if (dbgSubprogram->mBlock.IsEmpty()) return; int progSize = (int)(dbgSubprogram->mBlock.mHighPC - dbgSubprogram->mBlock.mLowPC); for (int progOffset = 0; true; progOffset += DBG_MAX_LOOKBACK) { int curSize = progSize - progOffset; if (curSize <= 0) break; BP_ALLOC_T(DbgSubprogramMapEntry); DbgSubprogramMapEntry* subprogramMapEntry = mAlloc.Alloc(); subprogramMapEntry->mAddress = dbgSubprogram->mBlock.mLowPC + progOffset; subprogramMapEntry->mEntry = dbgSubprogram; mDebugTarget->mSubprogramMap.Insert(subprogramMapEntry); } } bool DbgModule::ParseDWARF(const uint8*& dataPtr) { BP_ZONE("ParseDWARF"); const uint8* data = dataPtr; const uint8* startData = mDebugInfoData; int dataOfs = (int)(data - mDebugInfoData); intptr_target length = GET(int); DbgModule* linkedModule = GetLinkedModule(); if (length == -1) { mIsDwarf64 = true; length = (intptr_target)GET(int64); } else mIsDwarf64 = false; if (length == 0) return false; const uint8* dataEnd = data + length; int version = GET(short); int abbrevOffset = GET(int); char pointerSize = GET(char); ParseAbbrevData(mDebugAbbrevData + abbrevOffset); DbgCompileUnit* compileUnit = new DbgCompileUnit(this); mDbgFlavor = DbgFlavor_GNU; compileUnit->mDbgModule = this; mCompileUnits.push_back(compileUnit); DbgSubprogram* subProgram = NULL; //std::map typeMap; //std::map subprogramMap; int tagStart = (int)(data - startData); int tagEnd = (int)(dataEnd - startData); DbgDataMap dataMap(tagStart, tagEnd); DataStack dataStack; Array abstractOriginReplaceList; Array deferredArrayDims; int startingTypeIdx = (int)linkedModule->mTypes.size(); while (data < dataEnd) { gAbbrevNum++; const uint8* tagDataStart = data; int tagIdx = (int)(tagDataStart - startData); int abbrevIdx = (int)DecodeULEB128(data); const uint8* abbrevData = mDebugAbbrevPtrData[abbrevIdx]; if (abbrevIdx == 0) { if (deferredArrayDims.size() > 0) { DbgType* arrType = GetStackTop(&dataStack); BF_ASSERT(arrType->mTypeCode == DbgType_SizedArray); arrType->mSize = deferredArrayDims[0]; // Byte count still needs to be multiplied by the underlying type size DbgType* rootArrType = arrType; for (int dimIdx = 0; dimIdx < (int)deferredArrayDims.size() - 1; dimIdx++) { int dimSize = deferredArrayDims[dimIdx]; DbgType* subArrType = mAlloc.Alloc(); subArrType->mCompileUnit = compileUnit; subArrType->mLanguage = compileUnit->mLanguage; subArrType->mTypeIdx = (int)linkedModule->mTypes.size(); linkedModule->mTypes.push_back(subArrType); subArrType->mTypeCode = DbgType_SizedArray; subArrType->mTypeParam = arrType->mTypeParam; subArrType->mSize = deferredArrayDims[dimIdx + 1]; arrType->mTypeParam = subArrType; arrType = subArrType; } deferredArrayDims.Clear(); } dataStack.pop_back(); continue; } int entryTag = (int) DecodeULEB128(abbrevData); bool hasChildren = GET_FROM(abbrevData, char) == DW_CHILDREN_yes; int64 atLowPC = 0; int64 atHighPC = 0; int64 atRanges = 0; bool hasRanges = false; const uint8* atFrameBase = NULL; int64_t atFrameBaseLength = 0; int64 atLocationLen = 0; const uint8* atLocationData = 0; const char* atProducer = NULL; const char* atName = NULL; const char* atCompDir = NULL; const char* atLinkageName = NULL; int64 atConstValue = 0; int atDataMemberLocation = 0; const uint8* atDataMemberData = NULL; int atDeclFile = 0; int atDeclLine = 0; int atCallFile = 0; int atCallLine = 0; int atCount = 0; int atType = 0; int atImport = 0; int atInline = 0; int atArtificial = 0; int atExternal = 0; int atByteSize = -1; int atEncoding = 0; int atSpecification = 0; int atObjectPointer = 0; int atBitOffset = 0; int atBitSize = 0; int atAbstractOrigin = 0; const uint8* atVirtualLocData = NULL; bool atDeclaration = false; bool atVirtual = false; bool hadConstValue = false; bool hadMemberLocation = false; bool isOptimized = false; DataPair newDataPair; while (true) { int attrName = (int)DecodeULEB128(abbrevData); int form = (int)DecodeULEB128(abbrevData); if ((attrName == 0) && (form == 0)) break; switch (attrName) { case DW_AT_sibling: ReadValue(data, form); break; case DW_AT_location: atLocationLen = (int)ReadValue(data, form, dataOfs, &atLocationData, startData); break; case DW_AT_name: atName = ReadValue(data, form); break; case DW_AT_ordering: /*TODO:*/ ReadValue(data, form); break; case DW_AT_byte_size: atByteSize = ReadValue(data, form); break; case DW_AT_bit_offset: atBitOffset = ReadValue(data, form); break; case DW_AT_bit_size: atBitSize = ReadValue(data, form); break; case DW_AT_stmt_list: ReadValue(data, form); break; case DW_AT_low_pc: atLowPC = RemapAddr((addr_target)ReadValue(data, form)); break; case DW_AT_high_pc: atHighPC = ReadValue(data, form); break; case DW_AT_language: /*TODO:*/ ReadValue(data, form); break; case DW_AT_discr: /*TODO:*/ ReadValue(data, form); break; case DW_AT_discr_value: /*TODO:*/ ReadValue(data, form); break; case DW_AT_visibility: /*TODO:*/ ReadValue(data, form); break; case DW_AT_import: atImport = ReadValue(data, form) + dataOfs; break; case DW_AT_string_length: /*TODO:*/ ReadValue(data, form); break; case DW_AT_common_reference: /*TODO:*/ ReadValue(data, form); break; case DW_AT_comp_dir: atCompDir = ReadValue(data, form); break; case DW_AT_const_value: atConstValue = ReadValue(data, form); hadConstValue = true; break; case DW_AT_containing_type: /*TODO:*/ ReadValue(data, form); break; case DW_AT_default_value: /*TODO:*/ ReadValue(data, form); break; case DW_AT_inline: atInline = ReadValue(data, form); break; case DW_AT_is_optional: /*TODO:*/ ReadValue(data, form); break; case DW_AT_lower_bound: /*TODO:*/ ReadValue(data, form); break; case DW_AT_producer: atProducer = ReadValue(data, form); break; case DW_AT_prototyped: /*TODO:*/ ReadValue(data, form); break; case DW_AT_return_addr: /*TODO:*/ ReadValue(data, form); break; case DW_AT_start_scope: /*TODO:*/ ReadValue(data, form); break; case DW_AT_bit_stride: /*TODO:*/ ReadValue(data, form); break; case DW_AT_upper_bound: // Lower bound not supported atCount = ReadValue(data, form); break; case DW_AT_abstract_origin: atAbstractOrigin = ReadValue(data, form, dataOfs); break; case DW_AT_accessibility: /*TODO:*/ ReadValue(data, form); break; case DW_AT_address_class: /*TODO:*/ ReadValue(data, form); break; case DW_AT_artificial: atArtificial = ReadValue(data, form); break; case DW_AT_base_types: /*TODO:*/ ReadValue(data, form); break; case DW_AT_calling_convention: /*TODO:*/ ReadValue(data, form); break; case DW_AT_count: atCount = ReadValue(data, form); break; case DW_AT_data_member_location: if (form == DW_FORM_exprloc) { atDataMemberLocation = (int)ReadValue(data, form, dataOfs, &atDataMemberData); hadMemberLocation = true; } else { atDataMemberLocation = (int)ReadValue(data, form); hadMemberLocation = true; } break; case DW_AT_decl_column: /*TODO:*/ ReadValue(data, form); break; case DW_AT_decl_file: atDeclFile = ReadValue(data, form); break; case DW_AT_decl_line: atDeclLine = ReadValue(data, form); break; case DW_AT_declaration: atDeclaration = ReadValue(data, form); break; case DW_AT_discr_list: /*TODO:*/ ReadValue(data, form); break; case DW_AT_encoding: atEncoding = ReadValue(data, form); break; case DW_AT_external: atExternal = ReadValue(data, form); break; case DW_AT_frame_base: atFrameBaseLength = (int64_t)ReadValue(data, form, dataOfs, &atFrameBase); break; case DW_AT_friend: /*TODO:*/ ReadValue(data, form); break; case DW_AT_identifier_case: /*TODO:*/ ReadValue(data, form); break; case DW_AT_macro_info: /*TODO:*/ ReadValue(data, form); break; case DW_AT_namelist_item: /*TODO:*/ ReadValue(data, form); break; case DW_AT_priority: /*TODO:*/ ReadValue(data, form); break; case DW_AT_segment: /*TODO:*/ ReadValue(data, form); break; case DW_AT_specification: atSpecification = ReadValue(data, form, dataOfs); break; case DW_AT_static_link: /*TODO:*/ ReadValue(data, form); break; case DW_AT_type: atType = ReadValue(data, form, dataOfs); break; case DW_AT_use_location: /*TODO:*/ ReadValue(data, form); break; case DW_AT_variable_parameter: /*TODO:*/ ReadValue(data, form); break; case DW_AT_virtuality: atVirtual = ReadValue(data, form) != 0; break; case DW_AT_vtable_elem_location: ReadValue(data, form, dataOfs, &atVirtualLocData); break; case DW_AT_allocated: /*TODO:*/ ReadValue(data, form); break; case DW_AT_associated: /*TODO:*/ ReadValue(data, form); break; case DW_AT_data_location: /*TODO:*/ ReadValue(data, form); break; case DW_AT_byte_stride: /*TODO:*/ ReadValue(data, form); break; case DW_AT_entry_pc: /*TODO:*/ ReadValue(data, form); break; case DW_AT_use_UTF8: /*TODO:*/ ReadValue(data, form); break; case DW_AT_extension: /*TODO:*/ ReadValue(data, form); break; case DW_AT_ranges: atRanges = (int)ReadValue(data, form); hasRanges = true; break; case DW_AT_trampoline: /*TODO:*/ ReadValue(data, form); break; case DW_AT_call_column: /*TODO:*/ ReadValue(data, form); break; case DW_AT_call_file: atCallFile = ReadValue(data, form); break; case DW_AT_call_line: atCallLine = ReadValue(data, form); break; case DW_AT_description: /*TODO:*/ ReadValue(data, form); break; case DW_AT_binary_scale: /*TODO:*/ ReadValue(data, form); break; case DW_AT_decimal_scale: /*TODO:*/ ReadValue(data, form); break; case DW_AT_small: /*TODO:*/ ReadValue(data, form); break; case DW_AT_decimal_sign: /*TODO:*/ ReadValue(data, form); break; case DW_AT_digit_count: /*TODO:*/ ReadValue(data, form); break; case DW_AT_picture_string: /*TODO:*/ ReadValue(data, form); break; case DW_AT_mutable: /*TODO:*/ ReadValue(data, form); break; case DW_AT_threads_scaled: /*TODO:*/ ReadValue(data, form); break; case DW_AT_explicit: /*TODO:*/ ReadValue(data, form); break; case DW_AT_object_pointer: atObjectPointer = ReadValue(data, form); break; case DW_AT_endianity: /*TODO:*/ ReadValue(data, form); break; case DW_AT_elemental: /*TODO:*/ ReadValue(data, form); break; case DW_AT_pure: /*TODO:*/ ReadValue(data, form); break; case DW_AT_recursive: /*TODO:*/ ReadValue(data, form); break; case DW_AT_signature: /*TODO:*/ ReadValue(data, form); break; case DW_AT_main_subprogram: /*TODO:*/ ReadValue(data, form); break; case DW_AT_data_bit_offset: /*TODO:*/ ReadValue(data, form); break; case DW_AT_const_expr: /*TODO:*/ ReadValue(data, form); break; case DW_AT_enum_class: /*TODO:*/ ReadValue(data, form); break; case DW_AT_linkage_name: atLinkageName = ReadValue(data, form); break; // case DW_AT_MIPS_linkage_name: atLinkageName = ReadValue(data, form); break; case DW_AT_APPLE_optimized: isOptimized = ReadValue(data, form); break; default: ReadValue(data, form); break; } } if ((hasRanges) && (atLowPC == 0)) { addr_target* rangeData = (addr_target*)(mDebugRangesData + atRanges); while (true) { addr_target lowPC = *(rangeData++); if (lowPC == 0) break; addr_target highPC = *(rangeData++); if (compileUnit->mLowPC != (addr_target)-1) { // These are sometimes relative to the compile unit and sometimes absolute if (highPC + compileUnit->mLowPC <= compileUnit->mHighPC) { lowPC += compileUnit->mLowPC; highPC += compileUnit->mLowPC; } } highPC -= lowPC; // Select the largest range. We have some cases where some hoisting and such will // give us a small inlining aberration much earlier than expected so this ignores that if ((int64)highPC > atHighPC) { atLowPC = lowPC; atHighPC = highPC; } /*if ((atLowPC == 0) || (lowPC < (addr_target)atLowPC)) atLowPC = lowPC; if (highPC > (addr_target)atHighPC) atHighPC = highPC;*/ } } switch (entryTag) { case DW_TAG_compile_unit: { newDataPair = MakeDataPair(compileUnit); compileUnit->mName = atName; compileUnit->mProducer = atProducer; if (atCompDir != NULL) compileUnit->mCompileDir = atCompDir; if (atLowPC != 0) { compileUnit->mLowPC = (addr_target)atLowPC; compileUnit->mHighPC = (addr_target)(atLowPC + atHighPC); } if (compileUnit->mProducer.IndexOf("Beef") != -1) { compileUnit->mLanguage = DbgLanguage_Beef; } else { compileUnit->mLanguage = DbgLanguage_C; } compileUnit->mGlobalType->mLanguage = compileUnit->mLanguage; } break; case DW_TAG_imported_module: { DbgType* parentType = GetStackTop(&dataStack); DbgType* importType = GetOrCreateType(atImport, dataMap); if (parentType != NULL) // Parent type is NULL for Clang DbgModule info parentType->mUsingNamespaces.PushFront(importType, &mAlloc); } break; case DW_TAG_inlined_subroutine: case DW_TAG_subprogram: { /*//TODO: This is a test. See if it breaks anything. if ((atExternal != 0) && (atLowPC == 0)) break;*/ if (atSpecification == 0) { subProgram = GetOrCreate(tagIdx, dataMap); subProgram->mCompileUnit = compileUnit; subProgram->mVirtual = atVirtual; subProgram->mIsOptimized = isOptimized; if (atVirtualLocData != NULL) { const uint8* opPtr = atVirtualLocData; if (*(opPtr++) == DW_OP_constu) { subProgram->mVTableLoc = (int)DecodeSLEB128(opPtr) * sizeof(addr_target); } } //subProgram->mVTableLoc = atVirtualLoc * sizeof(addr_target); //SplitName(atName, subProgram->mName, subProgram->mTemplateName); subProgram->mName = atName; subProgram->mLinkName = atLinkageName; if (atAbstractOrigin != NULL) { DbgSubprogram* originSubProgram = GetOrCreate(atAbstractOrigin, dataMap); auto abstractOriginEntry = AbstractOriginEntry::Create(DbgSubprogram::ClassType, subProgram, originSubProgram); abstractOriginReplaceList.push_back(abstractOriginEntry); } subProgram->mParentType = GetStackTop(&dataStack); newDataPair = MakeDataPair(subProgram); //if ((atLinkageName != NULL) && (subProgram->mParentType != NULL)) //subProgram->mParentType->mDefinedMembersCount++; mSubprograms.push_back(subProgram); if (subProgram->mParentType != NULL) { subProgram->mParentType->mMethodList.PushBack(subProgram); } else { compileUnit->mGlobalType->mMethodList.PushBack(subProgram); } } else { subProgram = dataMap.Get(atSpecification); BF_ASSERT(subProgram != NULL); // We remove params form the declaration and re-add the real ones here subProgram->mParams.Clear(); } newDataPair = MakeDataPair(subProgram); DbgBlock* dwBlock = &subProgram->mBlock; if (atType != 0) subProgram->mReturnType = GetOrCreateType(atType, dataMap); if (!atDeclaration) { dwBlock->mLowPC = (addr_target)atLowPC; dwBlock->mHighPC = (addr_target)(atLowPC + atHighPC); if (dwBlock->mLowPC != 0) { compileUnit->mLowPC = std::min(compileUnit->mLowPC, dwBlock->mLowPC); compileUnit->mHighPC = std::max(compileUnit->mHighPC, dwBlock->mHighPC); } if (atObjectPointer != 0) subProgram->mHasThis = true; subProgram->mFrameBaseLen = (int)atFrameBaseLength; subProgram->mFrameBaseData = atFrameBase; if (atHighPC > 0) { MapSubprogram(subProgram); } } if (entryTag == DW_TAG_inlined_subroutine) { DbgSubprogram* parentSubProgram = GetStackLast(&dataStack); subProgram->mInlineeInfo = mAlloc.Alloc(); subProgram->mInlineeInfo->mInlineParent = parentSubProgram; subProgram->mInlineeInfo->mRootInliner = parentSubProgram->GetRootInlineParent(); subProgram->mFrameBaseData = parentSubProgram->mFrameBaseData; subProgram->mFrameBaseLen = parentSubProgram->mFrameBaseLen; } //if (subProgram->mParentType != NULL) //subProgram->mParentType->mDefinedMembersCount++; } break; case DW_TAG_lexical_block: { DbgBlock* prevBlock = GetStackTop(&dataStack); DbgBlock* dwBlock = mAlloc.Alloc(); if (hasRanges) { dwBlock->mLowPC = -1; dwBlock->mHighPC = (addr_target)atRanges; } else { dwBlock->mLowPC = (addr_target)atLowPC; dwBlock->mHighPC = (addr_target)(atLowPC + atHighPC); } newDataPair = MakeDataPair(dwBlock); prevBlock->mSubBlocks.PushBack(dwBlock); } break; case DW_TAG_variable: { DbgBlock* dwBlock = GetStackTop(&dataStack); if (atName && !strncmp(atName, "__asmLines", 10)) { const char* ptr = strchr(atName, '.'); if (!ptr) break; int declLine = atDeclLine; Array asmLines; int curAsmLine = 0; int curRunCount = 1; // initial value is starting line, with an assumed run count of 1 bool parity = true; // starting line is standalone; everything afterwards is in pairs while (true) { ++ptr; if (!*ptr) break; String s; if (*ptr == '$') { ++ptr; const char* dollarPtr = strchr(ptr, '$'); if (!dollarPtr) break; s = String(ptr, (int)(dollarPtr - ptr)); ptr = dollarPtr; } else { s += *ptr; } //int asmLine = atoi(s.c_str()); //asmLines.push_back(asmLine); const char* sPtr = s.c_str(); int decodedValue = (int)DecodeULEB32(sPtr); if (!parity) { curRunCount = decodedValue; } else { for (int iLine=0; iLinemAsmDebugLineMap == NULL) { mAsmDebugLineMaps.resize(mAsmDebugLineMaps.size() + 1); dwBlock->mAsmDebugLineMap = &mAsmDebugLineMaps.back(); } auto mapIter = dwBlock->mAsmDebugLineMap->find(declLine); if (mapIter != dwBlock->mAsmDebugLineMap->end()) { auto& dstVec = mapIter->second; dstVec.Reserve(dstVec.size() + asmLines.size()); //dstVec.insert(dstVec.end(), asmLines.begin(), asmLines.end()); if (!asmLines.IsEmpty()) dstVec.Insert(dstVec.size(), &asmLines[0], asmLines.size()); } else { (*dwBlock->mAsmDebugLineMap)[declLine] = std::move(asmLines); } break; } bool addToGlobalVarMap = false; bool isNewVariable = true; DbgVariable* dbgVariable = NULL; if (atSpecification != 0) { //dbgVariable = dataMap.Get(atSpecification); //BF_ASSERT(dbgVariable != NULL); dbgVariable = GetOrCreate(atSpecification, dataMap); //dbgVariable = dataMap.Get(atSpecification); //BF_ASSERT(dbgVariable != NULL); } else if (dwBlock != NULL) { dbgVariable = GetOrCreate(tagIdx, dataMap); dwBlock->mVariables.PushBack(dbgVariable); } else { DbgType* dbgType = GetStackTop(&dataStack); bool wantGlobal = true; if (compileUnit->mLanguage == DbgLanguage_Beef) { // Don't show certain global variables in Beef -- that includes things like VTable data if (atName[0] == '_') wantGlobal = false; } if ((dbgType == NULL) && (wantGlobal)) { /*DbgCompileUnit* topCompileUnit = GetStackTop(&dataStack); if (topCompileUnit != NULL) dbgType = &topCompileUnit->mGlobalType;*/ dbgType = linkedModule->mMasterCompileUnit->mGlobalType; auto foundEntry = mGlobalVarMap.Find(atName); if (foundEntry != NULL) { isNewVariable = false; dbgVariable = foundEntry->mValue; } else { addToGlobalVarMap = true; } } if (dbgVariable == NULL) dbgVariable = GetOrCreate(tagIdx, dataMap); dbgVariable->mIsStatic = true; //TODO: dbgType can be NULL. This only (apparently?) happens for DW_TAG_inlined_subroutine, which we don't handle right now... if (dbgType != NULL) { BF_ASSERT(dbgType->IsNamespace() || (dbgType->mTypeCode == DbgType_Root)); if (isNewVariable) dbgType->mMemberList.PushBack(dbgVariable); } } if (dbgVariable != NULL) { if (atSpecification == 0) { dbgVariable->mIsParam = false; dbgVariable->mName = atName; dbgVariable->mConstValue = atConstValue; dbgVariable->mType = GetOrCreateType(atType, dataMap); dbgVariable->mIsConst = hadConstValue; dbgVariable->mIsStatic = !hadMemberLocation; dbgVariable->mIsExtern = atExternal != 0; } if (atLinkageName != NULL) dbgVariable->mLinkName = atLinkageName; dbgVariable->mLocationLen = (int8)atLocationLen; dbgVariable->mLocationData = atLocationData; dbgVariable->mCompileUnit = compileUnit; /*if (dbgVariable->mIsStatic && !dbgVariable->mIsConst && (dbgVariable->mLocationLen > 0) && (dbgVariable->mIsExtern)) { DbgAddrType addrType = DbgAddrType_Value; // addr_target valAddr = mDebugTarget->EvaluateLocation(dbgVariable->mCompileUnit->mDbgModule, NULL, dbgVariable->mLocationData, dbgVariable->mLocationLen, NULL, &addrType); if ((addrType == DbgAddrType_Target) && (valAddr != 0)) { dbgVariable->mStaticCachedAddr = valAddr; if (dbgVariable->mLinkName != NULL) mStaticVariables.push_back(dbgVariable); } else dbgVariable->mIsStatic = false; }*/ // We had to remove the above for hot loading, calculate the mStaticCachedAddr later. Just put into mStaticVariables for now mStaticVariables.push_back(dbgVariable); if (atAbstractOrigin != NULL) { DbgVariable* originVariable = GetOrCreate(atAbstractOrigin, dataMap); auto abstractOriginEntry = AbstractOriginEntry::Create(DbgVariable::ClassType, dbgVariable, originVariable); if (atAbstractOrigin < tagIdx) abstractOriginEntry.Replace(); else abstractOriginReplaceList.push_back(abstractOriginEntry); } else if (dbgVariable->mName == NULL) dbgVariable->mName = "_unnamed"; if (addToGlobalVarMap) mGlobalVarMap.Insert(dbgVariable); newDataPair = MakeDataPair(dbgVariable); } } break; case DW_TAG_formal_parameter: { DbgSubprogram* dwSubprogram = GetStackTop(&dataStack); if (dwSubprogram == NULL) { if ((atName == NULL) && (atAbstractOrigin == 0)) { DbgType* dbgType = GetStackTop(&dataStack); if ((dbgType == NULL) || (dbgType->mTypeCode != DbgType_Subroutine)) break; //TODO: Add params to subroutine type break; } break; } if ((dwSubprogram->mParams.IsEmpty()) && (dwSubprogram->mParentType != 0)) dwSubprogram->mParentType->mMethodsWithParamsCount++; //DbgVariable* dbgVariable = mAlloc.Alloc(); DbgVariable* dwVariable = GetOrCreate(tagIdx, dataMap); dwSubprogram->mParams.PushBack(dwVariable); if (atArtificial != 0) { dwSubprogram->mHasThis = true; if (atName == NULL) atName = "this"; } dwVariable->mCompileUnit = compileUnit; dwVariable->mIsParam = true; dwVariable->mName = atName; dwVariable->mLocationLen = (int)atLocationLen; dwVariable->mLocationData = atLocationData; dwVariable->mType = GetOrCreateType(atType, dataMap); if (atAbstractOrigin != 0) { } } break; case DW_TAG_enumerator: { DbgVariable* member = mAlloc.Alloc(); member->mCompileUnit = compileUnit; member->mConstValue = atConstValue; member->mName = atName; member->mIsStatic = true; member->mIsConst = true; DbgType* parentType = GetStackTop(&dataStack); parentType->mMemberList.PushBack(member); member->mMemberOffset = atDataMemberLocation; //member->mType = parentType->mTypeParam; member->mType = parentType; // Insert into parent's namespace auto prevTop = dataStack.back(); dataStack.pop_back(); DbgBlock* dwBlock = GetStackTop(&dataStack); dataStack.push_back(prevTop); if (dwBlock != NULL) { DbgVariable* dwVariable = mAlloc.Alloc(); dwBlock->mVariables.PushBack(dwVariable); if (atSpecification == 0) { dwVariable->mIsParam = false; dwVariable->mName = atName; dwVariable->mConstValue = atConstValue; dwVariable->mType = parentType->mTypeParam; dwVariable->mIsConst = hadConstValue; dwVariable->mIsStatic = !hadMemberLocation; } dwVariable->mLocationLen = (int)atLocationLen; dwVariable->mLocationData = atLocationData; dwVariable->mCompileUnit = compileUnit; BF_ASSERT(dwVariable->mName != 0); newDataPair = MakeDataPair(dwVariable); } } break; /*case DW_TAG_subrange_type: { DbgType* parentType = GetStackTop(&dataStack); parentType->mArraySize = atUpperBound; } break;*/ case DW_TAG_inheritance: { DbgType* derivedType = GetStackTop(&dataStack); DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc(); baseTypeEntry->mBaseType = GetOrCreateType(atType, dataMap); if (atDataMemberData != NULL) { bool foundVirtOffset = false; const uint8* opPtr = atDataMemberData; if (*(opPtr++) == DW_OP_dup) { if (*(opPtr++) == DW_OP_deref) { if (*(opPtr++) == DW_OP_constu) { baseTypeEntry->mVTableOffset = (int)DecodeSLEB128(opPtr) / sizeof(int32); foundVirtOffset = true; if (*(opPtr++) == DW_OP_minus) baseTypeEntry->mVTableOffset = -baseTypeEntry->mVTableOffset; } } } BF_ASSERT(foundVirtOffset); } else baseTypeEntry->mThisOffset = atDataMemberLocation; derivedType->mBaseTypes.PushBack(baseTypeEntry); } break; case DW_TAG_member: { DbgType* parentType = GetStackTop(&dataStack); if ((atName != NULL) && (strncmp(atName, "_vptr$", 6) == 0)) { parentType->mHasVTable = true; break; } //DbgVariable* member = mAlloc.Alloc(); DbgVariable* member = GetOrCreate(tagIdx, dataMap); member->mIsMember = true; member->mCompileUnit = compileUnit; member->mName = atName; member->mType = GetOrCreateType(atType, dataMap); member->mConstValue = atConstValue; member->mIsConst = hadConstValue; member->mIsStatic = !hadMemberLocation; member->mBitSize = atBitSize; member->mBitOffset = atBitOffset; member->mIsExtern = atExternal != 0; parentType->mMemberList.PushBack(member); member->mMemberOffset = atDataMemberLocation; if ((member->mIsStatic) && (!member->mIsConst)) parentType->mHasStaticMembers = true; /*if ((member->mIsStatic) && (!member->mIsConst)) mStaticVariables.push_back(member);*/ newDataPair = MakeDataPair(member); //dataMap.Set(tagIdx, member); } break; case DW_TAG_subrange_type: { int typeIdx = (int)(tagDataStart - startData); DbgType* parentType = GetStackTop(&dataStack); int arrSize = atCount; deferredArrayDims.push_back(arrSize); } break; case DW_TAG_namespace: case DW_TAG_const_type: case DW_TAG_base_type: case DW_TAG_pointer_type: case DW_TAG_ptr_to_member_type: case DW_TAG_array_type: case DW_TAG_reference_type: case DW_TAG_rvalue_reference_type: case DW_TAG_unspecified_type: case DW_TAG_class_type: case DW_TAG_enumeration_type: case DW_TAG_structure_type: case DW_TAG_union_type: case DW_TAG_typedef: case DW_TAG_volatile_type: case DW_TAG_subroutine_type: //case DW_TAG_subrange_type: case DW_TAG_restrict_type: { int typeIdx = (int)(tagDataStart - startData); DbgType* dbgType = GetOrCreateType(typeIdx, dataMap); const char* nameSep = (compileUnit->mLanguage == DbgLanguage_Beef) ? "." : "::"; if ((atName != NULL) && ((entryTag == DW_TAG_structure_type) || (entryTag == DW_TAG_class_type) || (entryTag == DW_TAG_typedef) || (entryTag == DW_TAG_union_type) || (entryTag == DW_TAG_enumeration_type) || (entryTag == DW_TAG_namespace))) { BF_ASSERT(dbgType->mTypeCode == DbgType_Null); DbgType* parentType = GetStackTop(&dataStack); if (parentType != NULL) { dbgType->mParent = parentType; dbgType->mParent->mSubTypeList.PushBack(dbgType); /*if (dbgType->mParent->mName != NULL) { if (atName == NULL) { dbgType->mName = dbgType->mParent->mName; // Extend from name of parent if we're anonymous } else { int nameSepLen = strlen(nameSep); int parentNameLen = strlen(dbgType->mParent->mName); int nameLen = strlen(atName); char* name = (char*)mAlloc.AllocBytes(parentNameLen + nameSepLen + nameLen + 1); memcpy(name, dbgType->mParent->mName, parentNameLen); memcpy(name + parentNameLen, nameSep, nameSepLen); memcpy(name + parentNameLen + nameSepLen, atName, nameLen); dbgType->mName = name; } }*/ } else { // Add to global subtype list but don't set dbgType->mParent compileUnit->mGlobalType->mSubTypeList.PushBack(dbgType); } } const char* useName = atName; /*if ((useName != NULL) && (strcmp(useName, "@") == 0)) useName = NULL;*/ dbgType->mCompileUnit = compileUnit; dbgType->mLanguage = compileUnit->mLanguage; //SplitName(atName, dbgType->mTypeName, dbgType->mTemplateParams); dbgType->mName = useName; if (dbgType->mTypeName == NULL) dbgType->mTypeName = useName; //if (dbgType->mName == NULL) //dbgType->mName = atName; int parentNameLen = ((dbgType->mParent != NULL) && (dbgType->mParent->mName != NULL)) ? (int)strlen(dbgType->mParent->mName) : 0; int typeNameLen = (dbgType->mTypeName != NULL) ? (int)strlen(dbgType->mTypeName) : 0; //int templateParamsLen = (dbgType->mTemplateParams != NULL) ? strlen(dbgType->mTemplateParams) : 0; if ((parentNameLen != 0) /*&& (templateParamsLen == 0)*/) { int nameSepLen = (int)strlen(nameSep); int nameLen = parentNameLen + typeNameLen /*+ templateParamsLen*/; if ((parentNameLen > 0) && (nameLen > 0)) nameLen += nameSepLen; char* namePtr = (char*)mAlloc.AllocBytes(nameLen + 1, "DWARF"); dbgType->mName = namePtr; if (parentNameLen > 0) { memcpy(namePtr, dbgType->mParent->mName, parentNameLen); namePtr += parentNameLen; if (nameLen > 0) { memcpy(namePtr, nameSep, nameSepLen); namePtr += nameSepLen; } } if (nameLen > 0) { memcpy(namePtr, useName, typeNameLen); namePtr += typeNameLen; } /*if (templateParamsLen > 0) { memcpy(namePtr, dbgType->mTemplateParams, templateParamsLen); namePtr += templateParamsLen; }*/ } dbgType->mTypeCode = DbgType_Null; dbgType->mIsDeclaration = atDeclaration; if (atByteSize != -1) { dbgType->mSize = atByteSize; dbgType->mSizeCalculated = true; } switch (entryTag) { case DW_TAG_base_type: // Types that may do fallover to int/uints on size mismatch switch (atEncoding) { case DW_ATE_UTF: if (atByteSize == 1) dbgType->mTypeCode = DbgType_Utf8; else if (atByteSize == 2) dbgType->mTypeCode = DbgType_Utf16; else dbgType->mTypeCode = DbgType_Utf32; break; case DW_ATE_signed_char: if (atByteSize == 1) dbgType->mTypeCode = DbgType_SChar; else if (atByteSize == 2) dbgType->mTypeCode = DbgType_SChar16; else if (atByteSize == 4) dbgType->mTypeCode = DbgType_SChar32; else atEncoding = DW_ATE_signed; break; case DW_ATE_unsigned_char: if (atByteSize == 1) dbgType->mTypeCode = DbgType_UChar; else if (atByteSize == 2) dbgType->mTypeCode = DbgType_UChar16; else if (atByteSize == 4) dbgType->mTypeCode = DbgType_UChar32; atEncoding = DW_ATE_unsigned; break; case DW_ATE_boolean: if (atByteSize == 1) dbgType->mTypeCode = DbgType_Bool; else atEncoding = DW_ATE_unsigned; break; } if (dbgType->mTypeCode == DbgType_Null) { switch (atEncoding) { case DW_ATE_address: if (atByteSize == 0) dbgType->mTypeCode = DbgType_Void; break; case DW_ATE_boolean: if (atByteSize == 1) { dbgType->mTypeCode = DbgType_Bool; break; } //Fall through case DW_ATE_signed: switch (atByteSize) { case 1: dbgType->mTypeCode = DbgType_i8; break; case 2: dbgType->mTypeCode = DbgType_i16; break; case 4: dbgType->mTypeCode = DbgType_i32; break; case 8: dbgType->mTypeCode = DbgType_i64; break; case 16: dbgType->mTypeCode = DbgType_i128; break; } break; case DW_ATE_unsigned: switch (atByteSize) { case 1: dbgType->mTypeCode = DbgType_u8; break; case 2: if ((atName != NULL) && (strcmp(atName, "wchar_t") == 0)) dbgType->mTypeCode = DbgType_UChar16; else dbgType->mTypeCode = DbgType_u16; break; case 4: dbgType->mTypeCode = DbgType_u32; break; case 8: dbgType->mTypeCode = DbgType_u64; break; case 16: dbgType->mTypeCode = DbgType_u128; break; } break; case DW_ATE_float: if (atByteSize == 4) dbgType->mTypeCode = DbgType_Single; else if (atByteSize == 8) dbgType->mTypeCode = DbgType_Double; else if (atByteSize == 12) dbgType->mTypeCode = DbgType_Float96; else if (atByteSize == 16) dbgType->mTypeCode = DbgType_Float128; break; case DW_ATE_complex_float: if (atByteSize == 8) dbgType->mTypeCode = DbgType_ComplexFloat; else if (atByteSize == 16) dbgType->mTypeCode = DbgType_ComplexDouble; else if (atByteSize == 24) dbgType->mTypeCode = DbgType_ComplexDouble96; else if (atByteSize == 32) dbgType->mTypeCode = DbgType_ComplexDouble128; break; default: BF_FATAL("Unknown DW_ATE type"); break; } } break; case DW_TAG_enumeration_type: //TODO: Handle these differently dbgType->mTypeCode = DbgType_Enum; dbgType->mTypeParam = mAlloc.Alloc(); if (atByteSize == 8) dbgType->mTypeParam->mTypeCode = DbgType_i64; else if (atByteSize == 4) dbgType->mTypeParam->mTypeCode = DbgType_i32; else if (atByteSize == 2) dbgType->mTypeParam->mTypeCode = DbgType_i16; else if (atByteSize == 1) dbgType->mTypeParam->mTypeCode = DbgType_i8; else { BF_DBG_FATAL("Invalid enum type"); } break; case DW_TAG_namespace: dbgType->mTypeCode = DbgType_Namespace; break; case DW_TAG_const_type: dbgType->mTypeCode = DbgType_Const; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_rvalue_reference_type: dbgType->mTypeCode = DbgType_RValueReference; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_unspecified_type: dbgType->mTypeCode = DbgType_Unspecified; dbgType->mTypeName = atName; break; case DW_TAG_reference_type: dbgType->mTypeCode = DbgType_Ref; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_pointer_type: dbgType->mTypeCode = DbgType_Ptr; dbgType->mSize = sizeof(addr_target); dbgType->mSizeCalculated = true; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); if (dbgType->mTypeParam != NULL) dbgType->mTypeParam->mPtrType = dbgType; break; case DW_TAG_ptr_to_member_type: dbgType->mTypeCode = DbgType_PtrToMember; dbgType->mSize = sizeof(addr_target); dbgType->mTypeParam = GetOrCreateType(atType, dataMap); if (dbgType->mTypeParam != NULL) dbgType->mTypeParam->mPtrType = dbgType; break; case DW_TAG_array_type: dbgType->mTypeCode = DbgType_SizedArray; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_structure_type: dbgType->mTypeCode = DbgType_Struct; break; case DW_TAG_class_type: dbgType->mTypeCode = DbgType_Class; break; case DW_TAG_union_type: dbgType->mTypeCode = DbgType_Union; break; case DW_TAG_typedef: dbgType->mTypeCode = DbgType_TypeDef; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_volatile_type: dbgType->mTypeCode = DbgType_Volatile; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; case DW_TAG_subroutine_type: dbgType->mTypeCode = DbgType_Subroutine; if (atType != 0) // Return value dbgType->mTypeParam = GetOrCreateType(atType, dataMap); dbgType->mBlockParam = mAlloc.Alloc(); break; case DW_TAG_restrict_type: dbgType->mTypeCode = DbgType_Restrict; dbgType->mTypeParam = GetOrCreateType(atType, dataMap); break; } newDataPair = MakeDataPair(dbgType); } break; } if (hasChildren) dataStack.push_back(newDataPair); } for (auto& abstractOriginEntry : abstractOriginReplaceList) abstractOriginEntry.Replace(); GetLinkedModule()->MapTypes(startingTypeIdx); dataPtr = dataEnd; return true; } void DbgModule::ParseDebugFrameData() { BP_ZONE("ParseDebugFrameData"); const uint8* data = mDebugFrameData; if (data == NULL) return; mParsedFrameDescriptors = true; Dictionary commonFrameDescriptorMap; while (true) { addr_target relSectionAddr = (addr_target)(data - mDebugFrameData); int length = GET(int); if (length == 0) break; const uint8* dataEnd = data + length; int cieID = GET(int); if (cieID < 0) { BP_ALLOC_T(DwCommonFrameDescriptor); DwCommonFrameDescriptor* commonFrameDescriptor = mAlloc.Alloc(); char version = GET(char); commonFrameDescriptor->mDbgModule = this; commonFrameDescriptor->mAugmentation = DataGetString(data); if (version >= 4) { commonFrameDescriptor->mPointerSize = GET(int8); commonFrameDescriptor->mSegmentSize = GET(int8); } commonFrameDescriptor->mCodeAlignmentFactor = (int)DecodeULEB128(data); commonFrameDescriptor->mDataAlignmentFactor = (int)DecodeSLEB128(data); commonFrameDescriptor->mReturnAddressColumn = (int)DecodeULEB128(data); commonFrameDescriptor->mInstData = data; commonFrameDescriptor->mInstLen = (int)(dataEnd - data); mDebugTarget->mCommonFrameDescriptors.push_back(commonFrameDescriptor); if (version < 3) commonFrameDescriptorMap[relSectionAddr] = commonFrameDescriptor; else commonFrameDescriptorMap[mDebugFrameAddress + relSectionAddr] = commonFrameDescriptor; } else { addr_target lowPC = GET(addr_target); addr_target highPC = lowPC + GET(addr_target); DwCommonFrameDescriptor* commonFrameDescriptor = commonFrameDescriptorMap[(addr_target)cieID]; BF_ASSERT(commonFrameDescriptor != NULL); typedef decltype(mDebugTarget->mDwFrameDescriptorMap) MapType; auto resultPair = mDebugTarget->mDwFrameDescriptorMap.insert(MapType::value_type(lowPC, DwFrameDescriptor())); auto frameDescriptor = &resultPair.first->second; //frameDescriptor-> frameDescriptor->mLowPC = lowPC; frameDescriptor->mHighPC = highPC; frameDescriptor->mInstData = data; frameDescriptor->mInstLen = (int)(dataEnd - data); frameDescriptor->mCommonFrameDescriptor = commonFrameDescriptor; } data = dataEnd; } } void DbgModule::ParseEHFrameData() { const uint8* data = mEHFrameData; if (data == NULL) return; Dictionary commonFrameDescriptorMap; while (true) { addr_target sectionAddress = (addr_target)(data - mEHFrameData); int length = GET(int); if (length == 0) break; const uint8* dataEnd = data + length; int cieID = GET(int); if (cieID <= 0) { BP_ALLOC_T(DwCommonFrameDescriptor); DwCommonFrameDescriptor* commonFrameDescriptor = mAlloc.Alloc(); char version = GET(char); const char* augmentation = DataGetString(data); commonFrameDescriptor->mDbgModule = this; commonFrameDescriptor->mCodeAlignmentFactor = (int)DecodeULEB128(data); commonFrameDescriptor->mDataAlignmentFactor = (int)DecodeSLEB128(data); commonFrameDescriptor->mReturnAddressColumn = (int)DecodeULEB128(data); commonFrameDescriptor->mAugmentation = augmentation; if (*augmentation == 'z') { ++augmentation; int augLen = (int)DecodeULEB128(data); commonFrameDescriptor->mAugmentationLength = augLen; const uint8* augEnd = data + augLen; while (*augmentation != '\0') { if (*augmentation == 'R') commonFrameDescriptor->mAddressPointerEncoding = (int) GET(uint8); else if (*augmentation == 'P') { int encodingType = GET(uint8); BF_ASSERT(encodingType == 0); commonFrameDescriptor->mLSDARoutine = GET(addr_target); } else if (*augmentation == 'L') commonFrameDescriptor->mLSDAPointerEncodingFDE = GET(uint8); else if (*augmentation == 'S') { // mIsSignalHandler - on return from stack frame, CFA is before next instruction rather than after it } else BF_FATAL("Unknown CIE augmentation"); ++augmentation; } data = augEnd; } commonFrameDescriptor->mInstData = data; commonFrameDescriptor->mInstLen = (int)(dataEnd - data); mDebugTarget->mCommonFrameDescriptors.push_back(commonFrameDescriptor); commonFrameDescriptorMap[sectionAddress] = commonFrameDescriptor; } else { int ciePos = (int)(sectionAddress - cieID) + 4; DwCommonFrameDescriptor* commonFrameDescriptor = commonFrameDescriptorMap[(addr_target)ciePos]; addr_target lowPC; addr_target highPC; if (commonFrameDescriptor->mAddressPointerEncoding == (DW_EH_PE_pcrel | DW_EH_PE_sdata4)) { lowPC = GET(int); lowPC += mEHFrameAddress + sectionAddress + 8; highPC = lowPC + GET(int); } else { lowPC = GET(int); highPC = lowPC + GET(int); } typedef decltype(mDebugTarget->mDwFrameDescriptorMap) MapType; auto resultPair = mDebugTarget->mDwFrameDescriptorMap.insert(MapType::value_type(lowPC, DwFrameDescriptor())); auto frameDescriptor = &resultPair.first->second; frameDescriptor->mLSDARoutine = commonFrameDescriptor->mLSDARoutine; const char* augmentation = commonFrameDescriptor->mAugmentation; if (*augmentation == 'z') { int augLen = GET(uint8); const uint8* augEnd = data + augLen; ++augmentation; while (*augmentation != '\0') { if (*augmentation == 'R') { } else if (*augmentation == 'P') { } else if (*augmentation == 'L') { BF_ASSERT(commonFrameDescriptor->mLSDAPointerEncodingFDE == 0); frameDescriptor->mLSDARoutine = GET(addr_target); } else if (*augmentation == 'S') { } else BF_FATAL("Unknown CIE augmentation"); augmentation++; } data = augEnd; } frameDescriptor->mLowPC = lowPC; frameDescriptor->mHighPC = highPC; frameDescriptor->mInstData = data; frameDescriptor->mInstLen = (int)(dataEnd - data); frameDescriptor->mCommonFrameDescriptor = commonFrameDescriptor; } data = dataEnd; } } void DbgModule::FlushLineData(DbgSubprogram* curSubprogram, std::list& queuedLineData) { } DbgSrcFile* DbgModule::AddSrcFile(DbgCompileUnit* compileUnit, const String& srcFilePath) { DbgSrcFile* dwSrcFile = mDebugTarget->AddSrcFile(srcFilePath); if (compileUnit != NULL) { DbgSrcFileReference srcFileRef; srcFileRef.mSrcFile = dwSrcFile; srcFileRef.mCompileUnit = compileUnit; compileUnit->mSrcFileRefs.push_back(srcFileRef); } return dwSrcFile; } bool DbgModule::ParseDebugLineInfo(const uint8*& dataPtr, int compileUnitIdx) { BP_ZONE("ParseDebugLineInfo"); const uint8* data = dataPtr; const int startOffset = (int)(data - mDebugLineData); int length = GET(int); if (length == 0) return false; DbgCompileUnit* dwCompileUnit = mCompileUnits[compileUnitIdx]; const uint8* dataEnd = data + length; short version = GET(short); int headerLength = GET(int); char minimumInstructionLength = GET(char); int maximumOperationsPerInstruction = 1; char defaultIsStmt = GET(char); char lineBase = GET(char); char lineRange = GET(char); char opcodeBase = GET(char); for (int i = 0; i < opcodeBase - 1; i++) { char standardOpcodeLengths = GET(char); } Array directoryNames; while (true) { const char* name = DataGetString(data); if (name[0] == 0) break; directoryNames.push_back(name); } DbgSrcFileReference* dwSrcFileRef = NULL; HashSet foundPathSet; int curFileIdx = 0; DbgSubprogram* curSubprogram = NULL; #define ADD_LINEDATA(lineData) \ lineBuilder.Add(dwCompileUnit, lineData, dwSrcFileRef->mSrcFile, NULL); while (true) { const char* path = DataGetString(data); if (path[0] == 0) break; int directoryIdx = (int)DecodeULEB128(data); int lastModificationTime = (int)DecodeULEB128(data); int fileLength = (int)DecodeULEB128(data); String filePath; if (directoryIdx > 0) filePath = String(directoryNames[directoryIdx - 1]) + "/"; filePath += path; filePath = GetAbsPath(filePath, dwCompileUnit->mCompileDir); AddSrcFile(dwCompileUnit, filePath.c_str()); } if (dwCompileUnit->mSrcFileRefs.size() > 0) dwSrcFileRef = &dwCompileUnit->mSrcFileRefs.front(); DbgLineDataBuilder lineBuilder(this); bool queuedPostPrologue = false; DbgLineDataState dwLineData; dwLineData.mLine = 0; dwLineData.mRelAddress = 0; dwLineData.mOpIndex = 0; dwLineData.mBasicBlock = false; dwLineData.mDiscriminator = 0; dwLineData.mIsStmt = defaultIsStmt != 0; dwLineData.mIsa = 0; dwLineData.mColumn = -2; while (data < dataEnd) { uint8_t opcode = GET(uint8_t); switch (opcode) { case DW_LNS_extended_op: { int len = (int)DecodeULEB128(data); uint8_t exOpcode = GET(uint8_t); switch (exOpcode) { case DW_LNE_end_sequence: { ADD_LINEDATA(dwLineData); dwSrcFileRef = &dwCompileUnit->mSrcFileRefs[0]; dwLineData.mLine = 0; dwLineData.mRelAddress = 0; dwLineData.mOpIndex = 0; dwLineData.mBasicBlock = false; dwLineData.mDiscriminator = 0; dwLineData.mIsStmt = defaultIsStmt != 0; dwLineData.mIsa = 0; dwLineData.mColumn = -2; } break; case DW_LNE_set_address: dwLineData.mRelAddress = (uint32)(RemapAddr(GET(addr_target)) - mImageBase); break; case DW_LNE_define_file: { const char* path = DataGetString(data); int directoryIdx = (int)DecodeULEB128(data); int lastModificationTime = (int)DecodeULEB128(data); int fileLength = (int)DecodeULEB128(data); } break; case DW_LNE_set_discriminator: dwLineData.mDiscriminator = (int)DecodeULEB128(data); break; } } break; case DW_LNS_copy: ADD_LINEDATA(dwLineData); dwLineData.mDiscriminator = 0; dwLineData.mBasicBlock = false; break; case DW_LNS_advance_pc: { int advance = (int)DecodeULEB128(data); dwLineData.mRelAddress += advance; // How to advance opCode addr? } break; case DW_LNS_advance_line: { int advance = (int)DecodeSLEB128(data); dwLineData.mLine += advance; } break; case DW_LNS_set_file: { curFileIdx = (int)DecodeULEB128(data) - 1; dwSrcFileRef = &dwCompileUnit->mSrcFileRefs[curFileIdx]; //dwLineData.mSrcFileRef = dwSrcFileRef; } break; case DW_LNS_set_column: { dwLineData.mColumn = (int)DecodeULEB128(data) - 1; } break; case DW_LNS_negate_stmt: { dwLineData.mIsStmt = !dwLineData.mIsStmt; } break; case DW_LNS_set_basic_block: { dwLineData.mBasicBlock = true; } break; case DW_LNS_const_add_pc: { int adjustedOpcode = 255 - opcodeBase; int opAdvance = adjustedOpcode / lineRange; uint32 newAddress = dwLineData.mRelAddress + minimumInstructionLength * ((dwLineData.mOpIndex + opAdvance) / maximumOperationsPerInstruction); int newOpIndex = (dwLineData.mOpIndex + opAdvance) % maximumOperationsPerInstruction; dwLineData.mRelAddress = newAddress; dwLineData.mOpIndex = newOpIndex; } break; case DW_LNS_fixed_advance_pc: { uint16_t advance = GET(uint16_t); dwLineData.mRelAddress += advance; dwLineData.mOpIndex = 0; } break; case DW_LNS_set_prologue_end: { queuedPostPrologue = true; } break; case DW_LNS_set_epilogue_begin: { dwLineData.mColumn = -2; } break; case DW_LNS_set_isa: { dwLineData.mIsa = (int)DecodeULEB128(data); } break; default: { // Special opcode int adjustedOpcode = opcode - opcodeBase; int opAdvance = adjustedOpcode / lineRange; uint32 oldAddress = dwLineData.mRelAddress; uint32 newAddress = dwLineData.mRelAddress + minimumInstructionLength * ((dwLineData.mOpIndex + opAdvance) / maximumOperationsPerInstruction); int newOpIndex = (dwLineData.mOpIndex + opAdvance) % maximumOperationsPerInstruction; int lineIncrement = lineBase + (adjustedOpcode % lineRange); dwLineData.mLine += lineIncrement; dwLineData.mRelAddress = newAddress; dwLineData.mOpIndex = newOpIndex; DbgLineData* lastLineData = NULL; if ((newAddress == oldAddress) && (queuedPostPrologue) && (curSubprogram != NULL) && (curSubprogram->mBlock.mLowPC == newAddress)) { // Adjust this line later ADD_LINEDATA(dwLineData); } queuedPostPrologue = false; } break; } } lineBuilder.Commit(); dataPtr = data; return true; } addr_target DbgModule::GetHotTargetAddress(DbgHotTargetSection* hotTargetSection) { if ((hotTargetSection->mTargetSectionAddr == NULL) && (hotTargetSection->mDataSize > 0)) { if (hotTargetSection->mNoTargetAlloc) return 0; hotTargetSection->mTargetSectionAddr = mDebugger->AllocHotTargetMemory(hotTargetSection->mDataSize, hotTargetSection->mCanExecute, hotTargetSection->mCanWrite, &hotTargetSection->mTargetSectionSize); hotTargetSection->mImageOffset = (int)mImageSize; if (mImageBase == NULL) { mImageBase = hotTargetSection->mTargetSectionAddr; mOrigImageData->mAddr = mImageBase; } mImageSize += hotTargetSection->mTargetSectionSize; /*if (mExceptionData == hotTargetSection->mData) mExceptionDataRVA = (addr_target)(hotTargetSection->mTargetSectionAddr - mImageBase);*/ } return hotTargetSection->mTargetSectionAddr; } uint8* DbgModule::GetHotTargetData(addr_target address) { for (int sectNum = 0; sectNum < (int)mHotTargetSections.size(); sectNum++) { if (mHotTargetSections[sectNum] != NULL) { DbgHotTargetSection* hotTargetSection = mHotTargetSections[sectNum]; if ((address >= hotTargetSection->mTargetSectionAddr) && (address < hotTargetSection->mTargetSectionAddr + hotTargetSection->mTargetSectionSize)) { return hotTargetSection->mData + (address - hotTargetSection->mTargetSectionAddr); } } } return NULL; } void DbgModule::DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolvedSymbolAddr, PE_SymInfo* symInfo) { #ifdef BF_DBG_32 if (coffReloc.mType == IMAGE_REL_I386_DIR32) { *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += resolvedSymbolAddr; } else if (coffReloc.mType == IMAGE_REL_I386_DIR32NB) { GetHotTargetAddress(hotTargetSection); // Just to make sure we have mImageBase // We were previously using mImageBase instead of mDebugTarget->mTargetBinary->mImageBase. Was there a reason for that? // It was causing hot-loaded jump tables to have invalid addresses since the need to be relative to __ImageBase *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += (uint32)(resolvedSymbolAddr - GetTargetImageBase()); } else if (coffReloc.mType == IMAGE_REL_I386_REL32) { addr_target myAddr = GetHotTargetAddress(hotTargetSection) + coffReloc.mVirtualAddress; *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += resolvedSymbolAddr - myAddr - sizeof(int32); } else if (coffReloc.mType == IMAGE_REL_I386_SECTION) { // auto linkedModule = GetLinkedModule(); // addr_target mappedAddr = resolvedSymbolAddr & ~0x7FFFFFF; // int* encodingPtr = NULL; // if (linkedModule->mSecRelEncodingMap.TryAdd(mappedAddr, NULL, &encodingPtr)) // { // *encodingPtr = (int)linkedModule->mSecRelEncodingVec.size(); // linkedModule->mSecRelEncodingVec.push_back(mappedAddr); // } // *(uint16*)(hotTargetSection->mData + coffReloc.mVirtualAddress) = 0x8000 | *encodingPtr; *(uint16*)(hotTargetSection->mData + coffReloc.mVirtualAddress) = 0; } else if (coffReloc.mType == IMAGE_REL_I386_SECREL) { //*(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += symInfo->mValue; *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += resolvedSymbolAddr; } else { BF_ASSERT(0=="Invalid COFF reloc type"); } #else // CodeView uses SECTION:SECREL locations, and we just want to find a mapping such that // COFF::GetSectionAddr can map it to the 64-bit address. We do this by encoding the // lower 31 bits in the SECREL (allowing a 31-bit offset at the destination as well) // and then we use a 15-bit key to map the upper bits if (coffReloc.mType == IMAGE_REL_AMD64_REL32) { addr_target myAddr = GetHotTargetAddress(hotTargetSection) + coffReloc.mVirtualAddress; intptr_target addrOffset = resolvedSymbolAddr - myAddr - sizeof(int32); BF_ASSERT((int64)(int32)addrOffset == addrOffset); *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += (int32)addrOffset; } else if (coffReloc.mType == IMAGE_REL_AMD64_SECTION) { /*if (symInfo != NULL) { *(uint16*)(hotTargetSection->mData + coffReloc.mVirtualAddress) = symInfo->mSectionNum; } else*/ { auto linkedModule = GetLinkedModule(); addr_target mappedAddr = resolvedSymbolAddr & ~0x7FFFFFF; /*auto pair = linkedModule->mSecRelEncodingMap.insert(std::make_pair(mappedAddr, (int)linkedModule->mSecRelEncodingMap.size())); if (pair.second) linkedModule->mSecRelEncodingVec.push_back(mappedAddr);*/ int* encodingPtr = NULL; if (linkedModule->mSecRelEncodingMap.TryAdd(mappedAddr, NULL, &encodingPtr)) { *encodingPtr = (int)linkedModule->mSecRelEncodingVec.size(); linkedModule->mSecRelEncodingVec.push_back(mappedAddr); } //*(uint16*)(hotTargetSection->mData + coffReloc.mVirtualAddress) = 0x8000 | pair.first->second; *(uint16*)(hotTargetSection->mData + coffReloc.mVirtualAddress) = 0x8000 | *encodingPtr; } } else if (coffReloc.mType == IMAGE_REL_AMD64_SECREL) { auto linkedModule = GetLinkedModule(); if ((resolvedSymbolAddr >= linkedModule->mTLSAddr) && (resolvedSymbolAddr < linkedModule->mTLSAddr + linkedModule->mTLSSize)) { // Make relative to actual TLS data resolvedSymbolAddr -= linkedModule->mTLSAddr; } *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += (uint32)(resolvedSymbolAddr & 0x7FFFFFF); } else if (coffReloc.mType == IMAGE_REL_AMD64_ADDR64) { *(uint64*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += resolvedSymbolAddr; } else if (coffReloc.mType == IMAGE_REL_AMD64_ADDR32NB) { GetHotTargetAddress(hotTargetSection); // Just to make sure we have mImageBase // We were previously using mImageBase instead of mDebugTarget->mTargetBinary->mImageBase. Was there a reason for that? // It was causing hot-loaded jump tables to have invalid addresses since the need to be relative to __ImageBase *(uint32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += (uint32)(resolvedSymbolAddr - GetTargetImageBase()); //*(int32*)(hotTargetSection->mData + coffReloc.mVirtualAddress) += secRelAddr; } else { BF_ASSERT(0=="Invalid COFF reloc type"); } #endif } bool DbgModule::IsHotSwapPreserve(const String& name) { // We have different rules for overwriting symbols in DWARF vs CodeView // Since MS mangling includes return types, we know that a type change of a static // member will mangle to a new name whereas with DWARF we DO want a new // address if the type changes but we can't tell that based on the mangle alone, // thus the reliance on the side table of mStaticVariables. We still do need // to determine whether the symbol is data (and thus we do preserve) or a method // (in which case we don't) if ((mDbgFlavor == DbgFlavor_MS) && (BfDemangler::IsData(name))) { if ((!name.StartsWith("?")) && (name.Contains("sBfTypeData"))) // We DO need to replace the fields/methods/etc but not the base sBfTypeData return false; if (name.StartsWith("?bf_hs_replace_")) return false; return true; } const char* prefix = "bf_hs_preserve@"; return strncmp(name.c_str(), prefix, strlen(prefix)) == 0; } void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolvedSymbolAddrs) { auto mainModule = mDebugTarget->mTargetBinary; mainModule->ParseSymbolData(); String name; for (int sectNum = 0; sectNum < (int)mHotTargetSections.size(); sectNum++) { if (mHotTargetSections[sectNum] != NULL) { DbgHotTargetSection* hotTargetSection = mHotTargetSections[sectNum]; stream->SetPos(hotTargetSection->mPointerToRelocations); for (int relocIdx = 0; relocIdx < hotTargetSection->mNumberOfRelocations; relocIdx++) { COFFRelocation coffReloc; stream->Read(&coffReloc, sizeof(COFFRelocation)); PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18]; //const char* symName = mSymbolData[coffReloc.mSymbolTableIndex]; bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC; if (symInfo->mNameOfs[0] != 0) { if (symInfo->mName[7] != 0) { // Name is exactly 8 chars, not null terminated yet name = String(symInfo->mName, symInfo->mName + 8); } else name = symInfo->mName; } else name = mStringTable + symInfo->mNameOfs[1]; bool didNameMatch = false; addr_target resolvedSymbolAddr = resolvedSymbolAddrs[coffReloc.mSymbolTableIndex]; #ifdef BF_DBG_32 bool needsSymbolAddr = (coffReloc.mType == IMAGE_REL_I386_DIR32) || (coffReloc.mType == IMAGE_REL_I386_REL32) || (coffReloc.mType == IMAGE_REL_I386_SECREL) || (coffReloc.mType == IMAGE_REL_I386_SECTION); if (name[0] == '_') name.Remove(0, 1); #else bool needsSymbolAddr = (coffReloc.mType == IMAGE_REL_AMD64_ADDR64) || (coffReloc.mType == IMAGE_REL_AMD64_ADDR32) || (coffReloc.mType == IMAGE_REL_AMD64_ADDR32NB) || ((coffReloc.mType >= IMAGE_REL_AMD64_REL32) || (coffReloc.mType <= IMAGE_REL_AMD64_REL32_5)); #endif bool isHsPrev = false; if (name.StartsWith("bf_hs_prev@")) { isHsPrev = true; name.Remove(0, 11); } bool deferResolve = false; if ((resolvedSymbolAddr == 0) && (needsSymbolAddr)) { bool isHotSwapPreserve = IsHotSwapPreserve(name); if ((symInfo->mSectionNum == 0) || (isHotSwapPreserve) || (isHsPrev)) { auto origSymbolEntry = mainModule->mSymbolNameMap.Find(name.c_str()); if (origSymbolEntry != NULL) { resolvedSymbolAddr = origSymbolEntry->mValue->mAddress; } else { //BF_FATAL("Symbol lookup error"); deferResolve = true; } } if ((symInfo->mSectionNum != 0) && (resolvedSymbolAddr == NULL)) { DbgHotTargetSection* refHotTargetSection = mHotTargetSections[symInfo->mSectionNum - 1]; resolvedSymbolAddr = GetHotTargetAddress(refHotTargetSection) + symInfo->mValue; // Using the !hotTargetSection->mNoTargetAlloc check down here caused us to not properly remap reloaded // static members in the debug info. Even though we parse the debug info before we apply the deferred // resolves, the mLocData points into the original data so we still get it remapped when we use that // mLocData if (/*(!hotTargetSection->mNoTargetAlloc) &&*/ ((refHotTargetSection->mData == NULL) || (refHotTargetSection->mNoTargetAlloc)) && (!isStaticSymbol)) deferResolve = true; else deferResolve = false; } } if (deferResolve) { // It's a static field, defer resolution, but don't bother replacing for debug info sections DbgDeferredHotResolve* deferredResolve = mDeferredHotResolveList.Alloc(); deferredResolve->mHotTargetSection = hotTargetSection; deferredResolve->mName = name; deferredResolve->mNewAddr = resolvedSymbolAddr; deferredResolve->mReloc = coffReloc; continue; } else { resolvedSymbolAddrs[coffReloc.mSymbolTableIndex] = resolvedSymbolAddr; DoReloc(hotTargetSection, coffReloc, resolvedSymbolAddr, symInfo); } } } } } void DbgModule::CommitHotTargetSections() { for (int sectNum = 0; sectNum < (int)mHotTargetSections.size(); sectNum++) { if (mHotTargetSections[sectNum] != NULL) { DbgHotTargetSection* hotTargetSection = mHotTargetSections[sectNum]; addr_target hotAddr = GetHotTargetAddress(hotTargetSection); if (hotAddr != 0) { // void* imageDestPtr = mOrigImageData->mBlocks[0] + hotTargetSection->mImageOffset; // if (hotTargetSection->mData != NULL) // memcpy(imageDestPtr, hotTargetSection->mData, hotTargetSection->mDataSize); // else // memset(imageDestPtr, 0, hotTargetSection->mDataSize); BF_ASSERT(mOrigImageData->mAddr != 0); void* imageDestPtr = hotTargetSection->mData; bool isTemp = false; if (imageDestPtr == NULL) { imageDestPtr = new uint8[hotTargetSection->mDataSize]; memset(imageDestPtr, 0, hotTargetSection->mDataSize); isTemp = true; } if (hotTargetSection->mCanExecute) { bool success = mDebugger->WriteInstructions(hotAddr, imageDestPtr, hotTargetSection->mDataSize); BF_ASSERT(success); } else { bool success = mDebugger->WriteMemory(hotAddr, imageDestPtr, hotTargetSection->mDataSize); BF_ASSERT(success); } if (isTemp) delete imageDestPtr; } } } } void DbgModule::HotReplaceType(DbgType* newType) { auto linkedModule = GetLinkedModule(); newType->PopulateType(); DbgType* primaryType = linkedModule->GetPrimaryType(newType); if (primaryType == newType) { // There was no previous type BF_ASSERT(primaryType->mHotNewType == NULL); return; } if (primaryType->mHotNewType != newType) { // We have already pulled in the new data from a previous new type BF_ASSERT(primaryType->mHotNewType == NULL); return; } primaryType->mHotNewType = NULL; primaryType->PopulateType(); linkedModule->ParseGlobalsData(); linkedModule->ParseSymbolData(); if (primaryType->mNeedsGlobalsPopulated) { // These aren't proper TPI types so we don't have any method declarations until we PopulateTypeGlobals linkedModule->PopulateTypeGlobals(primaryType); } for (auto methodNameEntry : primaryType->mMethodNameList) { if (methodNameEntry->mCompileUnitId != -1) { linkedModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId); methodNameEntry->mCompileUnitId = -1; } } // Now actually remove the linedata from the defining module HashSet checkedFiles; for (auto method : primaryType->mMethodList) { //method->mWasModuleHotReplaced = true; method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned if (method->mLineInfo == NULL) continue; //FIXME: Hot replacing lines DbgSrcFile* lastSrcFile = NULL; checkedFiles.Clear(); int prevCtx = -1; auto inlineRoot = method->GetRootInlineParent(); for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++) { auto& lineData = method->mLineInfo->mLines[lineIdx]; if (lineData.mCtxIdx != prevCtx) { auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx]; auto srcFile = ctxInfo.mSrcFile; prevCtx = lineData.mCtxIdx; if (srcFile != lastSrcFile) { if (checkedFiles.Add(srcFile)) { // Remove linedata for old type // These go into a hot-replaced list so we can still bind to them -- that is necessary because // we may still have old versions of this method running (and may forever, if its in a loop on some thread) // since we only patch entry points //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true); //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true); srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true); } lastSrcFile = srcFile; } } } } //DbgType* primaryType = newType->GetPrimaryType(); // We need to keep a persistent list of hot replaced methods so we can set hot jumps // in old methods that may still be on the callstack. These entries get removed when // we unload unused hot files in while (!primaryType->mMethodList.IsEmpty()) { auto method = primaryType->mMethodList.PopFront(); method->PopulateSubprogram(); primaryType->mHotReplacedMethodList.PushFront(method); mHotPrimaryTypes.Add(primaryType); } Dictionary oldProgramMap; for (auto oldMethod : primaryType->mHotReplacedMethodList) { oldMethod->PopulateSubprogram(); if (oldMethod->mBlock.IsEmpty()) continue; auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC); if (symInfo != NULL) { oldProgramMap.TryAdd(symInfo->mName, oldMethod); } } bool setHotJumpFailed = false; while (!newType->mMethodList.IsEmpty()) { DbgSubprogram* newMethod = newType->mMethodList.PopFront(); if (!newMethod->mBlock.IsEmpty()) { newMethod->PopulateSubprogram(); auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC); if (symInfo != NULL) { DbgSubprogram* oldMethod = NULL; if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod)) { bool doHotJump = false; if (oldMethod->Equals(newMethod)) { doHotJump = true; } else { // When mangles match but the actual signatures don't match, that can mean that the call signature was changed // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed. // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard // fail if the old code gets called to avoid confusion of "why aren't my changes working?" // If we removed captures then we can still do the hot jump. Otherwise we have to fail... doHotJump = false; if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) && (oldMethod->mHasThis) && (newMethod->mHasThis)) { auto oldParam = oldMethod->mParams.front(); auto newParam = newMethod->mParams.front(); if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer())) { auto oldType = oldParam->mType->mTypeParam->GetPrimaryType(); oldType->PopulateType(); auto newType = newParam->mType->mTypeParam->GetPrimaryType(); newType->PopulateType(); if ((oldType->IsStruct()) && (newType->IsStruct())) { bool wasMatch = true; auto oldMember = oldType->mMemberList.front(); auto newMember = newType->mMemberList.front(); while (newMember != NULL) { if (oldMember == NULL) { wasMatch = false; break; } if ((oldMember->mName == NULL) || (newMember->mName == NULL)) { wasMatch = false; break; } if (strcmp(oldMember->mName, newMember->mName) != 0) { wasMatch = false; break; } if (!oldMember->mType->Equals(newMember->mType)) { wasMatch = false; break; } oldMember = oldMember->mNext; newMember = newMember->mNext; } if (wasMatch) doHotJump = true; } } if (!doHotJump) { mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC); oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid; } } } if (doHotJump) { if (!setHotJumpFailed) { if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC))) setHotJumpFailed = true; } oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced; } } } } newMethod->mParentType = primaryType; primaryType->mMethodList.PushBack(newMethod); } //mDebugTarget->mSymbolMap.Get() // bool setHotJumpFailed = false; // while (!newType->mMethodList.IsEmpty()) // { // DbgSubprogram* newMethod = newType->mMethodList.PopFront(); // if (!newMethod->mBlock.IsEmpty()) // { // newMethod->PopulateSubprogram(); // // bool found = false; // for (auto oldMethod : primaryType->mHotReplacedMethodList) // { // if (oldMethod->mBlock.IsEmpty()) // continue; // if (oldMethod->Equals(newMethod)) // { // if (!setHotJumpFailed) // { // if (!mDebugger->SetHotJump(oldMethod, newMethod)) // setHotJumpFailed = true; // oldMethod->mWasHotReplaced = true; // } // } // } // } // newMethod->mParentType = primaryType; // primaryType->mMethodList.PushBack(newMethod); // } primaryType->mCompileUnit->mWasHotReplaced = true; primaryType->mNeedsGlobalsPopulated = newType->mNeedsGlobalsPopulated; primaryType->mUsingNamespaces = newType->mUsingNamespaces; primaryType->mMemberList = newType->mMemberList; primaryType->mCompileUnit = newType->mCompileUnit; } bool DbgModule::CanRead(DataStream* stream, DebuggerResult* outResult) { PEHeader hdr; memset(&hdr, 0, sizeof(hdr)); PE_NTHeaders ntHdr; memset(&ntHdr, 0, sizeof(ntHdr)); stream->Read(&hdr, sizeof(PEHeader)); stream->SetPos(hdr.e_lfanew); stream->Read(&ntHdr, sizeof(PE_NTHeaders)); if ((hdr.e_magic != PE_DOS_SIGNATURE) || (ntHdr.mSignature != PE_NT_SIGNATURE)) { *outResult = DebuggerResult_UnknownError; return false; } #ifdef BF_DBG_32 if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X86) { if (ntHdr.mFileHeader.mMachine == PE_MACHINE_X64) *outResult = DebuggerResult_WrongBitSize; else *outResult = DebuggerResult_UnknownError; return false; } #else if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X64) { if (ntHdr.mFileHeader.mMachine == PE_MACHINE_X86) *outResult = DebuggerResult_WrongBitSize; else *outResult = DebuggerResult_UnknownError; return false; } #endif return true; } bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind) { BP_ZONE("DbgModule::ReadCOFF"); //if (this == mDebugTarget->mTargetBinary) //mMemReporter = new MemReporter(); if (mMemReporter != NULL) { mMemReporter->BeginSection(StrFormat("Module: %s", mFilePath.c_str())); mMemReporter->Add(mImageSize); } defer ( if (mMemReporter != NULL) mMemReporter->EndSection(); ); DbgModule* mainModule = mDebugTarget->mTargetBinary; MiniDumpDebugger* miniDumpDebugger = NULL; if (mDebugger->IsMiniDumpDebugger()) { miniDumpDebugger = (MiniDumpDebugger*)mDebugger; } mModuleKind = moduleKind; bool isHotSwap = mModuleKind == DbgModuleKind_HotObject; bool isObjectFile = mModuleKind != DbgModuleKind_Module; auto linkedModule = GetLinkedModule(); if (isObjectFile) linkedModule->PopulateStaticVariableMap(); mStartTypeIdx = (int)linkedModule->mTypes.size(); int startSrcFile = (int)mDebugTarget->mSrcFiles.size(); mStartSubprogramIdx = (int)mSubprograms.size(); PEHeader hdr; memset(&hdr, 0, sizeof(hdr)); PE_NTHeaders ntHdr; memset(&ntHdr, 0, sizeof(ntHdr)); if (!isObjectFile) { stream->Read(&hdr, sizeof(PEHeader)); stream->SetPos(hdr.e_lfanew); stream->Read(&ntHdr, sizeof(PE_NTHeaders)); mPreferredImageBase = ntHdr.mOptionalHeader.mImageBase; if (mImageBase == 0) { BF_ASSERT(this == mainModule); mImageBase = mPreferredImageBase; } if ((hdr.e_magic != PE_DOS_SIGNATURE) || (ntHdr.mSignature != PE_NT_SIGNATURE)) { mLoadState = DbgModuleLoadState_Failed; return false; } #ifdef BF_DBG_32 if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X86) return false; #else if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X64) { mLoadState = DbgModuleLoadState_Failed; return false; } #endif int pos = hdr.e_lfanew + FIELD_OFFSET(PE_NTHeaders, mOptionalHeader) + ntHdr.mFileHeader.mSizeOfOptionalHeader; stream->SetPos(pos); } else { stream->Read(&ntHdr.mFileHeader, sizeof(PEFileHeader)); if (mMemReporter != NULL) mMemReporter->Add("PEFileHeader", sizeof(PEFileHeader)); #ifdef BF_DBG_32 if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X86) return false; #else if (ntHdr.mFileHeader.mMachine != PE_MACHINE_X64) { mLoadState = DbgModuleLoadState_Failed; return false; } #endif } int sectionStartPos = stream->GetPos(); int sectionDataEndPos = 0; if (miniDumpDebugger != NULL) { // Map header miniDumpDebugger->MapMemory((addr_target)mImageBase, (uint8*)mMappedImageFile->mData, 0x1000); } stream->SetPos(sectionStartPos); for (int dirNum = 0; dirNum < (int) ntHdr.mFileHeader.mNumberOfSections; dirNum++) { PESectionHeader sectHdr; stream->Read(§Hdr, sizeof(PESectionHeader)); if (sectHdr.mSizeOfRawData > 0) sectionDataEndPos = sectHdr.mPointerToRawData + sectHdr.mSizeOfRawData; if (sectHdr.mNumberOfRelocations > 0) sectionDataEndPos = sectHdr.mPointerToRelocations + sectHdr.mNumberOfRelocations * sizeof(COFFRelocation); if (miniDumpDebugger != NULL) { miniDumpDebugger->MapMemory((addr_target)(mImageBase + sectHdr.mVirtualAddress), (uint8*)mMappedImageFile->mData + sectHdr.mPointerToRawData, sectHdr.mSizeOfRawData); } } //fseek(fp, sectionDataEndPos + ntHdr.mFileHeader.mNumberOfSymbols * 18, SEEK_SET); stream->SetPos(sectionDataEndPos); uint8* symbolData = new uint8[ntHdr.mFileHeader.mNumberOfSymbols * 18]; mAllocSizeData += ntHdr.mFileHeader.mNumberOfSymbols * 18; mSymbolData = symbolData; stream->Read(symbolData, ntHdr.mFileHeader.mNumberOfSymbols * 18); int curPos = stream->GetPos(); int strTableSize = 0; char* strTableData = NULL; if (!stream->Eof()) { stream->Read(&strTableSize, 4); if (strTableSize != 0) { strTableSize -= 4; strTableData = new char[strTableSize + 4]; memcpy(strTableData, &strTableSize, 4); stream->Read(strTableData + 4, strTableSize); mStringTable = strTableData; } } int mDebugFrameDataLen = 0; stream->SetPos(sectionStartPos); PEDataDirectory* exportDataDir = &ntHdr.mOptionalHeader.mDataDirectory[0]; mHotTargetSections.Resize(ntHdr.mFileHeader.mNumberOfSections); Array sectionHeaders; sectionHeaders.Resize(ntHdr.mFileHeader.mNumberOfSections); mSectionRVAs.Resize(sectionHeaders.size() + 1); Array sectionNames; sectionNames.Resize(ntHdr.mFileHeader.mNumberOfSections); stream->Read(§ionHeaders[0], sizeof(PESectionHeader) * ntHdr.mFileHeader.mNumberOfSections); for (int sectNum = 0; sectNum < ntHdr.mFileHeader.mNumberOfSections; sectNum++) { mSectionRVAs[sectNum] = sectionHeaders[sectNum].mVirtualAddress; } int tlsSection = -1; for (int sectNum = 0; sectNum < ntHdr.mFileHeader.mNumberOfSections; sectNum++) { //PEDataDirectory* dataDir = &ntHdr.mOptionalHeader.mDataDirectory[dirNum]; PESectionHeader& sectHdr = sectionHeaders[sectNum]; //stream->Read(§Hdr, sizeof(PESectionHeader)); char* name = sectHdr.mName; if (name[0] == '/') { int strIdx = atoi(name + 1); name = &strTableData[strIdx]; } sectionNames[sectNum] = name; DbgHotTargetSection* targetSection = NULL; if (IsObjectFile()) { targetSection = new DbgHotTargetSection(); targetSection->mDataSize = sectHdr.mSizeOfRawData; targetSection->mPointerToRelocations = sectHdr.mPointerToRelocations; targetSection->mNumberOfRelocations = sectHdr.mNumberOfRelocations; targetSection->mTargetSectionAddr = 0; // TODO: Allocate! targetSection->mCanExecute = (sectHdr.mCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0; targetSection->mCanWrite = (sectHdr.mCharacteristics & IMAGE_SCN_MEM_WRITE) != 0; targetSection->mNoTargetAlloc = (sectHdr.mCharacteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0; mHotTargetSections[sectNum] = targetSection; } DbgSection dwSection; dwSection.mIsExecutable = (sectHdr.mCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0; dwSection.mAddrStart = sectHdr.mVirtualAddress; dwSection.mAddrLength = BF_MAX(sectHdr.mSizeOfRawData, sectHdr.mVirtualSize); mSections.push_back(dwSection); if (sectHdr.mPointerToRawData == 0) continue; if (strcmp(name, ".tls") == 0) mTLSAddr = (addr_target)(sectHdr.mVirtualAddress + mImageBase); if ((IsObjectFile()) && (strcmp(name, ".tls$") == 0)) { tlsSection = sectNum; mTLSSize = sectHdr.mSizeOfRawData; targetSection->mNoTargetAlloc = true; } bool isExportDataDir = ((exportDataDir->mVirtualAddress != 0) && (exportDataDir->mVirtualAddress >= sectHdr.mVirtualAddress) && (exportDataDir->mVirtualAddress < sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData)); if ((!IsObjectFile()) && (!isExportDataDir)) { if (((strcmp(name, ".text")) == 0) || ((strcmp(name, ".textbss")) == 0) || ((strcmp(name, ".reloc")) == 0)/* || ((strcmp(name, ".data")) == 0)*/) { // Big unneeded sections continue; } } stream->SetPos(sectHdr.mPointerToRawData); int dataSize = sectHdr.mSizeOfRawData + 8; mAllocSizeData += dataSize; uint8* data = new uint8[dataSize]; { BP_ZONE("DbgModule::ReadCOFF_ReadSectionData"); stream->Read(data, sectHdr.mSizeOfRawData); } BfLogDbg("Read section data %s %p\n", name, data); memset(data + sectHdr.mSizeOfRawData, 0, 8); if (IsObjectFile()) targetSection->mData = data; addr_target addrOffset = sectHdr.mVirtualAddress; if (isExportDataDir) { BP_ZONE("DbgModule::ReadCOFF_SymbolMap"); IMAGE_EXPORT_DIRECTORY* imageExportDir = (IMAGE_EXPORT_DIRECTORY*)(data + (exportDataDir->mVirtualAddress - addrOffset)); for (int funcIdx = 0; funcIdx < (int)imageExportDir->NumberOfNames; funcIdx++) { //addr_target strAddr = *(addr_target*)(data + (imageExportDir->AddressOfNames - addrOffset) + funcIdx * sizeof(addr_target)); int32 strAddr = *(int32*)(data + (imageExportDir->AddressOfNames - addrOffset) + funcIdx * sizeof(int32)); const char* name = (const char*)(data + (strAddr - addrOffset)); #ifdef BF_DBG_32 if (name[0] == '_') name++; #endif int funcOrd = *(uint16*)(data + (imageExportDir->AddressOfNameOrdinals - addrOffset) + funcIdx * sizeof(uint16)); addr_target funcAddr = *(uint32*)(data + (imageExportDir->AddressOfFunctions - addrOffset) + funcOrd * sizeof(int32)); int strLen = (int)strlen(name); BP_ALLOC("ReadCOFF_SymbolMap", strLen + 1); char* allocStr = (char*)mAlloc.AllocBytes(strLen + 1, "ReadCOFF_SymbolMap"); memcpy(allocStr, name, strLen); BP_ALLOC_T(DbgSymbol); DbgSymbol* dwSymbol = mAlloc.Alloc(); dwSymbol->mDbgModule = this; dwSymbol->mName = allocStr; dwSymbol->mAddress = funcAddr; if (strcmp(name, "_tls_index") == 0) { mTLSIndexAddr = funcAddr; } //TODO: //mDeferredSymbols.PushFront(dwSymbol); dwSymbol->mAddress = (addr_target)(dwSymbol->mAddress + mImageBase); mDebugTarget->mSymbolMap.Insert(dwSymbol); linkedModule->mSymbolNameMap.Insert(dwSymbol); } } if ((IsObjectFile()) && (sectHdr.mNumberOfRelocations > 0)) { //mDebugger->AllocTargetMemory(sectHdr.mSizeOfRawData, true, true); } if (strcmp(name, ".text") == 0) { if (!IsObjectFile()) mCodeAddress = ntHdr.mOptionalHeader.mImageBase + sectHdr.mVirtualAddress; } //if (strcmp(name, ".rdata") == 0) { PEDataDirectory& debugDirEntry = ntHdr.mOptionalHeader.mDataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]; if (debugDirEntry.mSize > 0) { if (mMemReporter != NULL) mMemReporter->Add("DataDirectory", debugDirEntry.mSize); if ((debugDirEntry.mVirtualAddress >= sectHdr.mVirtualAddress) && (debugDirEntry.mVirtualAddress < sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData)) { int count = debugDirEntry.mSize / sizeof(IMAGE_DEBUG_DIRECTORY); for (int dirIdx = 0; dirIdx < count; dirIdx++) { IMAGE_DEBUG_DIRECTORY* debugDirectory = (IMAGE_DEBUG_DIRECTORY*)(data + debugDirEntry.mVirtualAddress - sectHdr.mVirtualAddress) + dirIdx; if (debugDirectory->Type == IMAGE_DEBUG_TYPE_CODEVIEW) { struct _CodeViewEntry { public: int32 mSig; uint8 mGUID[16]; int32 mAge; const char mPDBPath[1]; }; if (debugDirectory->AddressOfRawData != 0) { _CodeViewEntry* codeViewEntry = (_CodeViewEntry*)(data + debugDirectory->AddressOfRawData - sectHdr.mVirtualAddress); if (codeViewEntry->mSig == 'SDSR') { LoadPDB(codeViewEntry->mPDBPath, codeViewEntry->mGUID, codeViewEntry->mAge); } } } } } //stream->SetPos(debugDirEntry.mVirtualAddress); } } // { PEDataDirectory& tlsDirEntry = ntHdr.mOptionalHeader.mDataDirectory[IMAGE_DIRECTORY_ENTRY_TLS]; if (tlsDirEntry.mSize > 0) { if ((tlsDirEntry.mVirtualAddress >= sectHdr.mVirtualAddress) && (tlsDirEntry.mVirtualAddress < sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData)) { uint8* relPtr = data + tlsDirEntry.mVirtualAddress - sectHdr.mVirtualAddress; uint8* endPtr = relPtr + tlsDirEntry.mSize; addr_target tlsDataStart = GET_FROM(relPtr, addr_target) - ntHdr.mOptionalHeader.mImageBase; addr_target tlsDataEnd = GET_FROM(relPtr, addr_target) - ntHdr.mOptionalHeader.mImageBase; mTLSAddr = (addr_target)(tlsDataStart + mImageBase); mTLSSize = (int)(tlsDataEnd - tlsDataStart); } } } // { PEDataDirectory& debugDirEntry = ntHdr.mOptionalHeader.mDataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]; if (debugDirEntry.mSize > 0) { if ((debugDirEntry.mVirtualAddress >= sectHdr.mVirtualAddress) && (debugDirEntry.mVirtualAddress < sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData)) { uint8* relPtr = data + debugDirEntry.mVirtualAddress - sectHdr.mVirtualAddress; uint8* endPtr = relPtr + debugDirEntry.mSize; IMAGE_RESOURCE_DIRECTORY* typeDir = (IMAGE_RESOURCE_DIRECTORY*)(relPtr); // Skip named entries for (int typeIdx = 0; typeIdx < typeDir->NumberOfIdEntries; typeIdx++) { IMAGE_RESOURCE_DIRECTORY_ENTRY* typeEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((uint8*)typeDir + sizeof(IMAGE_RESOURCE_DIRECTORY) + (typeDir->NumberOfNamedEntries + typeIdx)*sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)); if (typeEntry->Id == 0x10) //VERSION { IMAGE_RESOURCE_DIRECTORY* idDir = (IMAGE_RESOURCE_DIRECTORY*)(relPtr + (typeEntry->OffsetToData & 0x7FFFFFFF)); if (idDir->NumberOfIdEntries < 1) break; IMAGE_RESOURCE_DIRECTORY_ENTRY* idEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((uint8*)idDir + sizeof(IMAGE_RESOURCE_DIRECTORY) + (idDir->NumberOfNamedEntries + 0) * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)); IMAGE_RESOURCE_DIRECTORY* langDir = (IMAGE_RESOURCE_DIRECTORY*)(relPtr + (idEntry->OffsetToData & 0x7FFFFFFF)); if (langDir->NumberOfIdEntries < 1) break; IMAGE_RESOURCE_DIRECTORY_ENTRY* langEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)((uint8*)langDir + sizeof(IMAGE_RESOURCE_DIRECTORY) + (langDir->NumberOfNamedEntries + 0) * sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY)); IMAGE_RESOURCE_DATA_ENTRY* dataEntry = (IMAGE_RESOURCE_DATA_ENTRY*)(relPtr + (langEntry->OffsetToData & 0x7FFFFFFF)); uint8* versionData = data + dataEntry->OffsetToData - sectHdr.mVirtualAddress; uint8* vPtr = versionData; auto vSize = GET_FROM(vPtr, uint16); auto verEnd = vPtr + vSize; auto vLength = GET_FROM(vPtr, uint16); vPtr += 36; // "VS_VERSION_INFO" auto fixedFileInfo = GET_FROM(vPtr, VS_FIXEDFILEINFO); auto _GetString = [&]() { wchar_t* cPtr = (wchar_t*)vPtr; int len = (int)wcslen(cPtr); vPtr += (len + 1) * 2; if (((intptr)vPtr & 3) != 0) vPtr += 2; UTF16String str16(cPtr, len); return UTF8Encode(str16); }; while (vPtr < verEnd) { auto size = GET_FROM(vPtr, uint16); auto childEnd = vPtr + size; auto valueLength = GET_FROM(vPtr, uint16); auto type = GET_FROM(vPtr, uint16); String infoType = _GetString(); if (infoType == "StringFileInfo") { while (vPtr < childEnd) { auto strsSize = GET_FROM(vPtr, uint16); auto strsEnd = vPtr + strsSize; auto strsLength = GET_FROM(vPtr, uint16); auto strsType = GET_FROM(vPtr, uint16); String hexNum = _GetString(); while (vPtr < strsEnd) { auto strSize = GET_FROM(vPtr, uint16); auto strEnd = vPtr + strSize; auto strLength = GET_FROM(vPtr, uint16); auto strType = GET_FROM(vPtr, uint16); String key = _GetString(); String value = _GetString(); if (key == "FileVersion") mVersion = value; } } } vPtr = childEnd; } } } } //stream->SetPos(debugDirEntry.mVirtualAddress); } } bool usedData = true; /*if (isUnwindSection) { mExceptionData = data; mExceptionDataRVA = sectHdr.mVirtualAddress; }*/ if (strcmp(name, ".pdata") == 0) { DbgSectionData entry; entry.mData = data; entry.mSize = sectHdr.mSizeOfRawData; mExceptionDirectory.Add(entry); } /*else if (strcmp(name, ".rdata") == 0) { if (mExceptionData == NULL) { mExceptionData = data; mExceptionDataRVA = sectHdr.mVirtualAddress; } else { usedData = false; } } else if (strcmp(name, ".xdata") == 0) { if (mExceptionData != NULL) { if (IsObjectFile()) { mOwnedSectionData.push_back(mExceptionData); } else { // xdata section overrides rdata exception data delete[] mExceptionData; } } mExceptionData = data; mExceptionDataRVA = sectHdr.mVirtualAddress; }*/ else if (strcmp(name, ".debug_info") == 0) { mDebugInfoData = data; } else if (strcmp(name, ".debug_line") == 0) { mDebugLineData = data; } else if (strcmp(name, ".debug_str") == 0) { mDebugStrData = data; } else if (strcmp(name, ".debug_frame") == 0) { mDebugFrameAddress = ntHdr.mOptionalHeader.mImageBase + sectHdr.mVirtualAddress; mDebugFrameData = data; mDebugFrameDataLen = sectHdr.mSizeOfRawData; } else if (strcmp(name, ".eh_frame") == 0) { mEHFrameAddress = ntHdr.mOptionalHeader.mImageBase + sectHdr.mVirtualAddress; mEHFrameData = data; } else if (strcmp(name, ".debug_abbrev") == 0) { mDebugAbbrevData = data; mDebugAbbrevPtrData = new const uint8*[sectHdr.mSizeOfRawData]; } else if (strcmp(name, ".debug_loc") == 0) { mDebugLocationData = data; } else if (strcmp(name, ".debug_ranges") == 0) { mDebugRangesData = data; } // else if (strcmp(name, ".rsrc") == 0) // { // //IMAGE_DIRECTORY_ENTRY_RESOURCE // } else if (CheckSection(name, data, sectHdr.mSizeOfRawData)) { // Was used } else { /*if (isUnwindSection) mOwnsExceptionData = true; else*/ usedData = false; } if (!usedData) { if (IsObjectFile()) { mOwnedSectionData.push_back(data); } else { mAllocSizeData -= dataSize; delete [] data; } } //stream->SetPos(prevPos); } int needHotTargetMemory = 0; if (isObjectFile) { for (int sectNum = 0; sectNum < ntHdr.mFileHeader.mNumberOfSections; sectNum++) { auto targetSection = mHotTargetSections[sectNum]; if (!targetSection->mNoTargetAlloc) needHotTargetMemory += (targetSection->mDataSize + (mDebugger->mPageSize - 1)) & ~(mDebugger->mPageSize - 1); } mDebugger->ReserveHotTargetMemory(needHotTargetMemory); // '0' address is temporary //mOrigImageData = new DbgModuleMemoryCache(0, NULL, needHotTargetMemory, true); mOrigImageData = new DbgModuleMemoryCache(0, needHotTargetMemory); } int numSections = ntHdr.mFileHeader.mNumberOfSections; if (isObjectFile) { addr_target* resolvedSymbolAddrs = new addr_target[ntHdr.mFileHeader.mNumberOfSymbols]; memset(resolvedSymbolAddrs, 0, ntHdr.mFileHeader.mNumberOfSymbols * sizeof(addr_target)); ParseHotTargetSections(stream, resolvedSymbolAddrs); delete [] resolvedSymbolAddrs; } ProcessDebugInfo(); if (mDebugInfoData != NULL) { mDbgFlavor = DbgFlavor_GNU; mMasterCompileUnit = new DbgCompileUnit(this); mMasterCompileUnit->mDbgModule = this; mMasterCompileUnit->mIsMaster = true; const uint8* data = mDebugInfoData; while (ParseDWARF(data)) {} CreateNamespaces(); // Must be added last so module reference indices still map correctly mCompileUnits.push_back(mMasterCompileUnit); } ParseDebugFrameData(); ParseEHFrameData(); mEndTypeIdx = (int)linkedModule->mTypes.size(); mEndSubprogramIdx = (int)mSubprograms.size(); if (mDebugLineData != NULL) { const uint8* data = mDebugLineData; for (int compileUnitIdx = 0; true; compileUnitIdx++) if (!ParseDebugLineInfo(data, compileUnitIdx)) break; } { BP_ZONE("ReadPE_ReadSymbols"); //linkedModule->mSymbolNameMap.reserve(linkedModule->mSymbolNameMap.size() + ntHdr.mFileHeader.mNumberOfSymbols * 2); bool tlsFailed = false; addr_target tlsMappingAddr = 0; for (int symNum = 0; symNum < (int)ntHdr.mFileHeader.mNumberOfSymbols; symNum++) { PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[symNum * 18]; char* name = symInfo->mName; if (symInfo->mNameOfs[0] != 0) { if (name[7] != 0) { // Name is exactly 8 chars, not null terminated yet name = (char*)mAlloc.AllocBytes(9, "PESymbol"); memcpy(name, symInfo->mName, 8); name[8] = 0; } } else name = strTableData + symInfo->mNameOfs[1]; if ((symInfo->mStorageClass == COFF_SYM_CLASS_EXTERNAL) || (symInfo->mStorageClass == COFF_SYM_CLASS_STATIC)) { // 'static' in the C sense. // It means local to the compile unit, so may have multiple copies of the same symbol name. bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC; if (symInfo->mSectionNum == 0xFFFF) continue; if (symInfo->mSectionNum > 0) { bool isTLS = false; addr_target targetAddr = 0; if (isObjectFile) { if (symInfo->mSectionNum - 1 == tlsSection) { isTLS = true; } else { auto hotTargetSection = mHotTargetSections[symInfo->mSectionNum - 1]; if (hotTargetSection != NULL) targetAddr = GetHotTargetAddress(hotTargetSection) + symInfo->mValue; } } else targetAddr = mSectionRVAs[symInfo->mSectionNum - 1] + symInfo->mValue; if (((targetAddr != 0) || (isTLS)) && (name[0] != '.')) { const char* symbolName = name; #ifdef BF_DBG_32 if (symbolName[0] == '_') symbolName++; #endif if (strcmp(symbolName, "_tls_index") == 0) { mTLSIndexAddr = (addr_target)(targetAddr + mImageBase); } if ((isStaticSymbol) && (IsHotSwapPreserve(symbolName))) isStaticSymbol = false; if ((isObjectFile) && (!isStaticSymbol)) { DbgSymbol* dwSymbol = NULL; linkedModule->ParseSymbolData() ; BP_ALLOC_T(DbgSymbol); dwSymbol = mAlloc.Alloc(); dwSymbol->mDbgModule = this; dwSymbol->mName = symbolName; dwSymbol->mAddress = targetAddr; if (dwSymbol != NULL) { bool isHotSwapPreserve = IsHotSwapPreserve(dwSymbol->mName); bool insertIntoNameMap = true; bool oldFound = false; auto nameMapEntry = linkedModule->mSymbolNameMap.Find(dwSymbol->mName); if (nameMapEntry != NULL) { oldFound = true; if (!isHotSwapPreserve) { nameMapEntry->mValue = dwSymbol; } else if (mDbgFlavor == DbgFlavor_MS) { // Store in our own map - this is needed for storing address of the new vdata // so the new values can be copied in mSymbolNameMap.Insert(dwSymbol); } } else { if (isTLS) { if (mainModule->mTLSExtraAddr == 0) { auto extraSym = mainModule->mSymbolNameMap.Find("__BFTLS_EXTRA"); if (extraSym != NULL) { mainModule->ParseGlobalsData(); auto itr = mainModule->mStaticVariableMap.find("__BFTLS_EXTRA"); if (itr != mainModule->mStaticVariableMap.end()) { auto staticVar = itr->second; mainModule->mTLSExtraAddr = extraSym->mValue->mAddress; mainModule->mTLSExtraSize = (int)staticVar->mType->GetByteCount(); } } } if ((mainModule->mTLSExtraAddr != 0) && (tlsMappingAddr == 0)) { // Take a chunk out of __BFTLS_EXTRA if (mTLSSize <= mainModule->mTLSExtraSize) { tlsMappingAddr = mainModule->mTLSExtraAddr; mainModule->mTLSExtraAddr += mTLSSize; mainModule->mTLSExtraSize -= mTLSSize; } } if (tlsMappingAddr != 0) { BF_ASSERT(symInfo->mValue < mTLSSize); dwSymbol->mAddress = tlsMappingAddr + symInfo->mValue; } if (dwSymbol->mAddress == 0) { if (!tlsFailed) { Fail(StrFormat("Hot swapping failed to allocate TLS address for '%s'. Program restart required.", name)); } dwSymbol->mAddress = (addr_target)0xCDCDCDCD; tlsFailed = true; } } } if (dwSymbol->mAddress != 0) { if (!oldFound) linkedModule->mSymbolNameMap.Insert(dwSymbol); mDebugTarget->mSymbolMap.Insert(dwSymbol); } } } else { //TODO: We don't need to defer symbols anymore... we can just do a Fixup on their addr //mDeferredSymbols.PushFront(dwSymbol); BP_ALLOC_T(DbgSymbol); DbgSymbol* dwSymbol = mAlloc.Alloc(); dwSymbol->mDbgModule = this; dwSymbol->mName = symbolName; dwSymbol->mAddress = targetAddr; if (!IsObjectFile()) dwSymbol->mAddress += (addr_target)mImageBase; if (IsObjectFile()) BF_ASSERT((dwSymbol->mAddress >= mImageBase) && (dwSymbol->mAddress < mImageBase + mImageSize)); mDebugTarget->mSymbolMap.Insert(dwSymbol); if (!isStaticSymbol) linkedModule->mSymbolNameMap.Insert(dwSymbol); } } } } if (symInfo->mStorageClass == COFF_SYM_CLASS_FILE) { const char* fileName = (const char*)&mSymbolData[(symNum + 1) * 18]; } symNum += symInfo->mNumOfAuxSymbols; } } int subProgramSizes = 0; for (int subProgramIdx = mStartSubprogramIdx; subProgramIdx < mEndSubprogramIdx; subProgramIdx++) { auto dwSubprogram = mSubprograms[subProgramIdx]; subProgramSizes += (int)(dwSubprogram->mBlock.mHighPC - dwSubprogram->mBlock.mLowPC); /*for (int i = 0; i < dwSubprogram->mLineDataArray.mSize; i++) { auto lineData = dwSubprogram->mLineDataArray.mData[i]; auto srcFile = lineData->mSrcFileRef->mSrcFile; srcFile->mLineData.push_back(lineData); srcFile->mHadLineData = true; if ((srcFile->mFirstLineDataDbgModule == NULL) || (srcFile->mFirstLineDataDbgModule == this)) srcFile->mFirstLineDataDbgModule = this; else srcFile->mHasLineDataFromMultipleModules = true; }*/ } // Delete srcFiles without line data int lineDataCount = 0; /*for (int srcFileIdx = startSrcFile; srcFileIdx < (int)mDebugTarget->mSrcFiles.size(); srcFileIdx++) { if (!mDebugTarget->mSrcFiles[srcFileIdx]->mHadLineData) { mEmptySrcFiles.push_back(mDebugTarget->mSrcFiles[srcFileIdx]); mDebugTarget->mSrcFiles.erase(mDebugTarget->mSrcFiles.begin() + srcFileIdx); } else lineDataCount += (int)mDebugTarget->mSrcFiles[srcFileIdx]->mLineData.size(); }*/ auto srcFilesItr = mDebugTarget->mSrcFiles.begin(); while (srcFilesItr != mDebugTarget->mSrcFiles.end()) { DbgSrcFile* srcFile = srcFilesItr->mValue; if ((!srcFile->mHadLineData) && (srcFile->mLocalPath.IsEmpty())) { mEmptySrcFiles.push_back(srcFile); srcFilesItr = mDebugTarget->mSrcFiles.Remove(srcFilesItr); } else { ++srcFilesItr; } } if (!isObjectFile) { mImageSize = ntHdr.mOptionalHeader.mSizeOfImage; mEntryPoint = ntHdr.mOptionalHeader.mAddressOfEntryPoint; } /*OutputDebugStrF("%s:\n CompileUnits:%d DebugLines: %d Types: %d (%d in map) SubPrograms: %d (%dk) AllocSize:%dk\n", mFilePath.c_str(), mCompileUnits.size(), lineDataCount, mEndTypeIdx - mStartTypeIdx, (int)linkedModule->mTypes.size() - mStartTypeIdx, mEndSubprogramIdx - mStartSubprogramIdx, subProgramSizes / 1024, mAlloc.GetAllocSize() / 1024);*/ if (isHotSwap) { // In COFF, we don't necessarily add an actual primary type during MapCompileUnitMethods, so this fixes that while (true) { bool didReplaceType = false; for (auto itr = mHotPrimaryTypes.begin(); itr != mHotPrimaryTypes.end(); ++itr) { auto dbgType = *itr; auto primaryType = dbgType->GetPrimaryType(); if (primaryType != dbgType) { mHotPrimaryTypes.Remove(itr); mHotPrimaryTypes.Add(primaryType); didReplaceType = true; break; } } if (!didReplaceType) break; } BF_ASSERT(mTypes.size() == 0); for (int typeIdx = mStartTypeIdx; typeIdx < (int)linkedModule->mTypes.size(); typeIdx++) { DbgType* newType = linkedModule->mTypes[typeIdx]; //if (!newType->mMethodList.IsEmpty()) if (!newType->mIsDeclaration) HotReplaceType(newType); } } if (needHotTargetMemory != 0) { BF_ASSERT(needHotTargetMemory == mImageSize); } //BF_ASSERT(mEndTypeIdx == (int)linkedModule->mTypes.size()); //BF_ASSERT(mEndSubprogramIdx == (int)mSubprograms.size()); ParseExceptionData(); mLoadState = DbgModuleLoadState_Loaded; if (mMemReporter != NULL) { mMemReporter->BeginSection("Sections"); ParseSymbolData(); Array orderedSyms; for (auto sym : mSymbolNameMap) { auto dbgSym = sym->mValue; orderedSyms.Add(dbgSym); } orderedSyms.Sort([](DbgSymbol* lhs, DbgSymbol* rhs) { return lhs->mAddress < rhs->mAddress; }); for (int sectNum = 0; sectNum < ntHdr.mFileHeader.mNumberOfSections; sectNum++) { PESectionHeader& sectHdr = sectionHeaders[sectNum]; mMemReporter->BeginSection(sectionNames[sectNum]); DbgSymbol* lastSym = NULL; for (auto dbgSym : orderedSyms) { if (dbgSym->mAddress < mImageBase + sectHdr.mVirtualAddress) continue; if (dbgSym->mAddress >= mImageBase + sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData) break; if (lastSym != NULL) { mMemReporter->Add(lastSym->mName, (int)(dbgSym->mAddress - lastSym->mAddress)); } else { int startingOffset = (int)(dbgSym->mAddress - (mImageBase + sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData)); if (startingOffset > 0) mMemReporter->Add("", startingOffset); } lastSym = dbgSym; } if (lastSym != NULL) mMemReporter->Add(lastSym->mName, (int)((mImageBase + sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData) - lastSym->mAddress)); else { mMemReporter->Add("", (int)(sectHdr.mSizeOfRawData)); } mMemReporter->EndSection(); } mMemReporter->EndSection(); mMemReporter->mShowInKB = false; mMemReporter->Report(); } return true; } void DbgModule::FinishHotSwap() { BF_ASSERT(IsObjectFile()); auto linkedModule = GetLinkedModule(); auto mainModule = mDebugTarget->mTargetBinary; HashSet failSet; String findName; for (auto deferredHotResolve : mDeferredHotResolveList) { addr_target resolveTargetAddr = deferredHotResolve->mNewAddr; findName = deferredHotResolve->mName; if (mDbgFlavor == DbgFlavor_MS) { // ... why do we need to find these variables in the variable map instead of the symbol name map? } auto itr = mainModule->mStaticVariableMap.find(findName.c_str()); if (itr != mainModule->mStaticVariableMap.end()) { DbgVariable* variable = itr->second; resolveTargetAddr = mDebugTarget->GetStaticAddress(variable); } else { auto symbolEntry = mainModule->mSymbolNameMap.Find(findName.c_str()); if (symbolEntry != NULL) { resolveTargetAddr = symbolEntry->mValue->mAddress; } else { if (deferredHotResolve->mName == "__ImageBase") { resolveTargetAddr = (addr_target)mainModule->mImageBase; } else { resolveTargetAddr = mainModule->LocateSymbol(deferredHotResolve->mName); if (resolveTargetAddr == 0) { failSet.Add(deferredHotResolve->mName); continue; } } } } DoReloc(deferredHotResolve->mHotTargetSection, deferredHotResolve->mReloc, resolveTargetAddr, NULL); } mDeferredHotResolveList.Clear(); if (!failSet.IsEmpty()) { bool handled = false; if (!mDebugger->mDebugManager->mOutMessages.empty()) { auto& str = mDebugger->mDebugManager->mOutMessages.back(); if (str.Contains("failed to resolve")) { for (auto& sym : failSet) { str += ", "; str += sym; } handled = true; } } if (!handled) { int symIdx = 0; String str; if (failSet.size() == 1) str = "Hot swapping failed to resolve symbol: "; else str = "Hot swapping failed to resolve symbols: "; for (auto& sym : failSet) { if (symIdx != 0) str += ", "; str += sym; symIdx++; } mDebugger->Fail(str); } } CommitHotTargetSections(); // We need this here because vdata gets loaded first, so we need to wait until we have the addrs for the new methods (from other modules) // before we can finalize the class vdata. ProcessHotSwapVariables(); for (auto hotTargetSection : mHotTargetSections) delete hotTargetSection; mHotTargetSections.Clear(); mSymbolNameMap.Clear(); } addr_target DbgModule::ExecuteOps(DbgSubprogram* dwSubprogram, const uint8* locData, int locDataLen, WdStackFrame* stackFrame, CPURegisters* registers, DbgAddrType* outAddrType, DbgEvalLocFlags flags, addr_target* pushValue) { bool allowReg = (flags & DbgEvalLocFlag_IsParam) == 0; const uint8* locDataEnd = locData + locDataLen; int regNum = -1; addr_target stackFrameData[256]; int stackIdx = 0; if (pushValue != NULL) stackFrameData[stackIdx++] = *pushValue; while (locData < locDataEnd) { uint8 opCode = GET_FROM(locData, uint8); switch (opCode) { case DW_OP_piece: { if (*outAddrType == DbgAddrType_Register) *outAddrType = DbgAddrType_Value; addr_target val = stackFrameData[--stackIdx]; int pieceSize = (int)DecodeULEB128(locData); if (pieceSize == 4) val &= 0xFFFFFFFF; else if (pieceSize == 2) val &= 0xFFFF; else if (pieceSize == 1) val &= 0xFF; stackFrameData[stackIdx++] = val; } break; case DW_OP_consts: { int64 val = DecodeSLEB128(locData); stackFrameData[stackIdx++] = (addr_target)val; } break; case DW_OP_stack_value: { *outAddrType = DbgAddrType_Value; } break; case DW_OP_addr_noRemap: { addr_target addr = GET_FROM(locData, addr_target); stackFrameData[stackIdx++] = addr; //*outIsAddr = true; *outAddrType = DbgAddrType_Target; } break; case DW_OP_addr: { addr_target addr = GET_FROM(locData, addr_target); //if (dwarf != NULL) addr = RemapAddr(addr); stackFrameData[stackIdx++] = addr; //*outIsAddr = true; *outAddrType = DbgAddrType_Target; } break; case DW_OP_deref: { addr_target addr = stackFrameData[--stackIdx]; addr_target value = mDebugger->ReadMemory(addr); stackFrameData[stackIdx++] = value; } break; case DW_OP_fbreg: { if (registers == NULL) return 0; BF_ASSERT(dwSubprogram != NULL); DbgSubprogram* nonInlinedSubProgram = dwSubprogram->GetRootInlineParent(); if (nonInlinedSubProgram->mFrameBaseData == NULL) { *outAddrType = DbgAddrType_Target; //TODO: why? return 0; } BF_ASSERT(nonInlinedSubProgram->mFrameBaseData != NULL); uint64 loc = EvaluateLocation(nonInlinedSubProgram, nonInlinedSubProgram->mFrameBaseData, nonInlinedSubProgram->mFrameBaseLen, stackFrame, outAddrType, DbgEvalLocFlag_DisallowReg); int64 offset = DecodeSLEB128(locData); loc += offset; //loc = BfDebuggerReadMemory(loc); //*outIsAddr = true; *outAddrType = DbgAddrType_Target; stackFrameData[stackIdx++] = (addr_target)loc; } break; case DW_OP_reg0: case DW_OP_reg1: case DW_OP_reg2: case DW_OP_reg3: case DW_OP_reg4: case DW_OP_reg5: case DW_OP_reg6: case DW_OP_reg7: case DW_OP_reg8: case DW_OP_reg9: case DW_OP_reg10: case DW_OP_reg11: case DW_OP_reg12: case DW_OP_reg13: case DW_OP_reg14: case DW_OP_reg15: if (registers == NULL) return 0; BF_ASSERT((opCode - DW_OP_reg0) < CPURegisters::kNumIntRegs); regNum = opCode - DW_OP_reg0; stackFrameData[stackIdx++] = registers->mIntRegsArray[regNum]; *outAddrType = DbgAddrType_Register; break; case DW_OP_reg21: //XMM0 BF_FATAL("XMM registers not supported yet"); break; case DW_OP_breg0: case DW_OP_breg1: case DW_OP_breg2: case DW_OP_breg3: case DW_OP_breg4: case DW_OP_breg5: case DW_OP_breg6: case DW_OP_breg7: case DW_OP_breg8: case DW_OP_breg9: case DW_OP_breg10: case DW_OP_breg11: case DW_OP_breg12: case DW_OP_breg13: case DW_OP_breg14: case DW_OP_breg15: { if (registers == NULL) return 0; int64 offset = DecodeSLEB128(locData); BF_ASSERT((opCode - DW_OP_breg0) < CPURegisters::kNumIntRegs); auto loc = registers->mIntRegsArray[opCode - DW_OP_breg0] + offset; //loc = BfDebuggerReadMemory(loc); //*outIsAddr = true; *outAddrType = DbgAddrType_Target; stackFrameData[stackIdx++] = (addr_target)loc; } break; case DW_OP_bregx: { if (registers == NULL) return 0; int regNum = (int)DecodeULEB128(locData); int64 offset = DecodeSLEB128(locData); BF_ASSERT(regNum < CPURegisters::kNumIntRegs); auto loc = registers->mIntRegsArray[regNum] + offset; //loc = BfDebuggerReadMemory(loc); //*outIsAddr = true; *outAddrType = DbgAddrType_Target; stackFrameData[stackIdx++] = (addr_target)loc; } break; case DW_OP_const4u: { uint32 val = GET_FROM(locData, uint32); stackFrameData[stackIdx++] = val; } break; case DW_OP_const8u: { uint64 val = GET_FROM(locData, uint64); stackFrameData[stackIdx++] = (addr_target)val; } break; case DW_OP_GNU_push_tls_address: { if ((mTLSAddr == 0) || (mTLSIndexAddr == 0)) return 0; int tlsIndex = mDebugger->ReadMemory(mTLSIndexAddr); addr_target tlsEntry = mDebugger->GetTLSOffset(tlsIndex); intptr_target tlsValueIndex = stackFrameData[--stackIdx]; stackFrameData[stackIdx++] = (tlsValueIndex - mTLSAddr) + tlsEntry; *outAddrType = DbgAddrType_Target; } break; case DW_OP_nop: break; default: BF_FATAL("Unknown DW_OP"); break; } } if (*outAddrType == DbgAddrType_Register) { if (allowReg) return regNum; *outAddrType = DbgAddrType_Value; } //BF_ASSERT(stackIdx == 1); return stackFrameData[--stackIdx]; } addr_target DbgModule::EvaluateLocation(DbgSubprogram* dwSubprogram, const uint8* locData, int locDataLen, WdStackFrame* stackFrame, DbgAddrType* outAddrType, DbgEvalLocFlags flags) { BP_ZONE("DebugTarget::EvaluateLocation"); auto dbgModule = this; if (locDataLen == DbgLocationLenKind_SegPlusOffset) { BF_ASSERT(dbgModule->mDbgFlavor == DbgFlavor_MS); if (dbgModule->mDbgFlavor == DbgFlavor_MS) { COFF* coff = (COFF*)dbgModule; struct SegOfsData { uint32 mOfs; uint16 mSeg; }; SegOfsData* segOfsData = (SegOfsData*)locData; *outAddrType = DbgAddrType_Target; return coff->GetSectionAddr(segOfsData->mSeg, segOfsData->mOfs); } else { *outAddrType = DbgAddrType_Target; return 0; } } CPURegisters* registers = NULL; if (stackFrame != NULL) registers = &stackFrame->mRegisters; if (locDataLen < 0) { if (registers == NULL) return 0; int64 ipAddr = stackFrame->GetSourcePC(); const uint8* checkLocData = locData; int64 startLoc = (int64)GET_FROM(checkLocData, addr_target); int64 endLoc = startLoc + GET_FROM(checkLocData, uint16); BF_ASSERT(dwSubprogram != NULL); startLoc += dwSubprogram->mCompileUnit->mLowPC; endLoc += dwSubprogram->mCompileUnit->mLowPC; if ((ipAddr >= startLoc) && (ipAddr < endLoc)) { locDataLen = -locDataLen - sizeof(addr_target) - sizeof(uint16); locData = checkLocData; } else { *outAddrType = DbgAddrType_OptimizedOut; return 0; } } else if (locDataLen == 0) { if (registers == NULL) return 0; int64 ipAddr = stackFrame->GetSourcePC(); const uint8* checkLocData = locData; while (true) { int64 startLoc = (int64)GET_FROM(checkLocData, addr_target); int64 endLoc = (int64)GET_FROM(checkLocData, addr_target); if ((startLoc == 0) && (endLoc == 0)) { *outAddrType = DbgAddrType_OptimizedOut; return 0; } BF_ASSERT(dwSubprogram != NULL); startLoc += dwSubprogram->mCompileUnit->mLowPC; endLoc += dwSubprogram->mCompileUnit->mLowPC; if ((ipAddr >= startLoc) && (ipAddr < endLoc)) { locDataLen = GET_FROM(checkLocData, int16); locData = checkLocData; break; } else { int len = GET_FROM(checkLocData, int16);; checkLocData += len; } } } return ExecuteOps(dwSubprogram, locData, locDataLen, stackFrame, registers, outAddrType, flags); } void DbgModule::ProcessHotSwapVariables() { BP_ZONE("DbgModule::ProcessHotSwapVariables"); auto linkedModule = GetLinkedModule(); for (auto staticVariable : mStaticVariables) { bool replaceVariable = false; const char* findName = staticVariable->GetMappedName(); auto itr = linkedModule->mStaticVariableMap.find(findName); if (itr != linkedModule->mStaticVariableMap.end()) { DbgVariable* oldVariable = itr->second; // If the old static field has the same type as the new static field then we keep the same // address, otherwise we use the new (zeroed-out) allocated space auto _GetNewAddress = [&]() { addr_target newAddress = 0; if (mDbgFlavor == DbgFlavor_GNU) { newAddress = mDebugTarget->GetStaticAddress(staticVariable); } else { // In CodeView, the newVariable ends up pointing to the old address, so we need to store // the location in our own mSymbolNameMap auto entry = mSymbolNameMap.Find(oldVariable->mLinkName); if (entry != NULL) newAddress = entry->mValue->mAddress; } return newAddress; }; if (oldVariable->mType->IsSizedArray()) { mDebugTarget->GetCompilerSettings(); bool doMerge = strstr(oldVariable->mName, "sBfClassVData") != NULL; bool keepInPlace = (doMerge) && (strstr(oldVariable->mName, ".vext") == NULL); if (doMerge) { addr_target oldAddress = mDebugTarget->GetStaticAddress(oldVariable); addr_target newAddress = _GetNewAddress(); if (newAddress == 0) continue; uint8* newData = GetHotTargetData(newAddress); int newArraySize = (int)staticVariable->mType->GetByteCount(); int oldArraySize = (int)oldVariable->mType->GetByteCount(); int copySize = std::min(newArraySize, oldArraySize); BF_ASSERT((oldArraySize & (sizeof(addr_target) - 1)) == 0); DbgModule* defModule = oldVariable->mType->mCompileUnit->mDbgModule; defModule->EnableWriting(oldAddress); uint8* mergedData = new uint8[copySize]; mDebugger->ReadMemory(oldAddress, copySize, mergedData); // The new vtable may have 0's in it when virtual methods are removed. Keep the old virtual addresses in those. addr_target* newDataPtr = (addr_target*)newData; addr_target* mergedPtr = (addr_target*)mergedData; while (mergedPtr < (addr_target*)(mergedData + copySize)) { if (*newDataPtr != 0) *mergedPtr = *newDataPtr; mergedPtr++; newDataPtr++; } bool success; success = mDebugger->WriteMemory(oldAddress, mergedData, copySize); BF_ASSERT(success); memcpy(newData, mergedData, copySize); delete mergedData; } else if (strstr(oldVariable->mName, "sStringLiterals") != NULL) { addr_target oldAddress = mDebugTarget->GetStaticAddress(oldVariable); addr_target newAddress = NULL; if (mDbgFlavor == DbgFlavor_GNU) { newAddress = mDebugTarget->GetStaticAddress(staticVariable); } else { // In CodeView, the newVariable ends up pointing to the old address, so we need to store // the location in our own mSymbolNameMap auto entry = mSymbolNameMap.Find(oldVariable->mLinkName); if (entry == NULL) continue; newAddress = entry->mValue->mAddress; } // Make sure newAddress doesn't have anything linked to it addr_target val = 0; bool success = mDebugger->ReadMemory((intptr)newAddress, sizeof(addr_target), &val); BF_ASSERT(success); BF_ASSERT(val == 0); // Link the new table to the old extended table addr_target prevLinkage = 0; success = mDebugger->ReadMemory((intptr)oldAddress, sizeof(addr_target), &prevLinkage); BF_ASSERT(success); success = mDebugger->WriteMemory((intptr)newAddress, &prevLinkage, sizeof(addr_target)); BF_ASSERT(success); mDebugger->EnableWriting((intptr)oldAddress, sizeof(addr_target)); success = mDebugger->WriteMemory((intptr)oldAddress, &newAddress, sizeof(addr_target)); BF_ASSERT(success); keepInPlace = true; } if (keepInPlace) { // We have to maintain the OLD size because we can't overwrite the original bounds staticVariable->mType = oldVariable->mType; staticVariable->mLocationLen = oldVariable->mLocationLen; staticVariable->mLocationData = oldVariable->mLocationData; staticVariable->mCompileUnit = oldVariable->mCompileUnit; } } else if (oldVariable->mType->Equals(staticVariable->mType)) { if (oldVariable->mType->IsStruct()) { if ((strncmp(oldVariable->mName, "?sBfTypeData@", 13) == 0) || (strncmp(oldVariable->mName, "sBfTypeData.", 12) == 0)) { int size = (int)staticVariable->mType->GetByteCount(); addr_target oldAddress = mDebugTarget->GetStaticAddress(oldVariable); addr_target newAddress = _GetNewAddress(); if (newAddress == 0) continue; uint8* data = new uint8[size]; bool success = mDebugger->ReadMemory(newAddress, size, data); if (success) { mDebugger->EnableWriting((intptr)oldAddress, size); success = mDebugger->WriteMemory(oldAddress, data, size); } delete data; BF_ASSERT(success); staticVariable->mLocationLen = oldVariable->mLocationLen; staticVariable->mLocationData = oldVariable->mLocationData; } } //staticVariable->mLocationLen = oldVariable->mLocationLen; //staticVariable->mLocationData = oldVariable->mLocationData; replaceVariable = false; } else { BF_ASSERT(!oldVariable->mType->IsSizedArray()); } if (!replaceVariable) { auto symbolVal = linkedModule->mSymbolNameMap.Find(staticVariable->GetMappedName()); if (symbolVal != NULL) { addr_target oldAddress = mDebugTarget->GetStaticAddress(oldVariable); DbgSymbol* oldSymbol = mDebugTarget->mSymbolMap.Get(oldAddress); if (oldSymbol != NULL) symbolVal->mValue = oldSymbol; } } } else // Not found - new variable replaceVariable = true; if (replaceVariable) { linkedModule->mStaticVariableMap[staticVariable->GetMappedName()] = staticVariable; } } } int64 DbgModule::GetImageSize() { return mImageSize; } /*const uint8* DbgModule::GetOrigImageData(addr_target address) { return mOrigImageData + (address - mImageBase); }*/ DbgFileExistKind DbgModule::CheckSourceFileExist(const StringImpl& path) { DbgFileExistKind existsKind = DbgFileExistKind_NotFound; if (FileExists(path)) existsKind = DbgFileExistKind_Found; String oldSourceCommand = GetOldSourceCommand(path); if (!oldSourceCommand.IsEmpty()) { int crPos = (int)oldSourceCommand.IndexOf('\n'); if (crPos != -1) { String targetPath = oldSourceCommand.Substring(0, crPos); if (FileExists(targetPath)) existsKind = DbgFileExistKind_Found; else existsKind = DbgFileExistKind_HasOldSourceCommand; } } return existsKind; } void DbgModule::EnableWriting(addr_target address) { for (int sectionIdx = 0; sectionIdx < (int)mSections.size(); sectionIdx++) { DbgSection* section = &mSections[sectionIdx]; if ((address >= mImageBase + section->mAddrStart) && (address < mImageBase + section->mAddrStart + section->mAddrLength)) { if (!section->mWritingEnabled) { section->mOldProt = mDebugger->EnableWriting(mImageBase + section->mAddrStart, (int32)section->mAddrLength); section->mWritingEnabled = true; } } } } void DbgModule::RevertWritingEnable() { for (int sectionIdx = 0; sectionIdx < (int)mSections.size(); sectionIdx++) { DbgSection* section = &mSections[sectionIdx]; if (section->mWritingEnabled) { mDebugger->SetProtection(mImageBase + section->mAddrStart, (int32)section->mAddrLength, section->mOldProt); section->mWritingEnabled = false; } } } template static void RemoveInvalidRange(TRadixMap& radixMap, addr_target startAddr, int addrLength) { radixMap.RemoveRange(startAddr, addrLength); } template static void RemoveInvalidMapRange(TMap& map, addr_target startAddr, int addrLength) { auto itr = map.lower_bound(startAddr); while (itr != map.end()) { auto val = itr->first; if (val >= startAddr + addrLength) return; itr = map.erase(itr); } } void DbgModule::RemoveTargetData() { BP_ZONE("DbgModule::RemoveTargetData"); for (auto srcFileRef : mSrcFileDeferredRefs) srcFileRef->RemoveDeferredRefs(this); HashSet visitedFiles; for (auto compileUnit : mCompileUnits) { for (auto& fileRef : compileUnit->mSrcFileRefs) { if (visitedFiles.Add(fileRef.mSrcFile)) { fileRef.mSrcFile->RemoveLines(this); } } } RemoveInvalidRange(mDebugTarget->mSymbolMap, (addr_target)mImageBase, (int32)mImageSize); RemoveInvalidRange(mDebugTarget->mSubprogramMap, (addr_target)mImageBase, (int32)mImageSize); RemoveInvalidRange(mDebugTarget->mExceptionDirectoryMap, (addr_target)mImageBase, (int32)mImageSize); RemoveInvalidRange(mDebugTarget->mContribMap, (addr_target)mImageBase, (int32)mImageSize); RemoveInvalidMapRange(mDebugTarget->mDwFrameDescriptorMap, (addr_target)mImageBase, (int32)mImageSize); RemoveInvalidMapRange(mDebugTarget->mCOFFFrameDescriptorMap, (addr_target)mImageBase, (int32)mImageSize); //mDebugTarget->mDwFrameDescriptorMap.erase() // Remove any of our entries from the mHotReplacedMethodList from 'primary modules' that are not going away for (auto dbgType : mHotPrimaryTypes) { DbgSubprogram** nextSrc = &dbgType->mHotReplacedMethodList.mHead; while (*nextSrc != NULL) { auto* subprogram = *nextSrc; if (subprogram->mCompileUnit->mDbgModule == this) *nextSrc = subprogram->mNext; else nextSrc = &(*nextSrc)->mNext;; } } } void DbgModule::ReportMemory(MemReporter* memReporter) { //memReporter->Add("BumpAlloc_Used", mAlloc.GetAllocSize()); //memReporter->Add("BumpAlloc_Unused", mAlloc.GetTotalAllocSize() - mAlloc.GetAllocSize()); memReporter->AddBumpAlloc("BumpAlloc", mAlloc); memReporter->AddVec(mTypes); memReporter->AddVec(mSubprograms); //memReporter->Add("TypeMap", mTypeMap.mAlloc.GetTotalAllocSize() + sizeof(StrHashMap)); memReporter->AddHashSet("TypeMap", mTypeMap.mMap); memReporter->Add("SymbolNameMap", mSymbolNameMap.mAlloc.GetTotalAllocSize() + sizeof(StrHashMap)); if (mOrigImageData != NULL) { memReporter->BeginSection("OrigImageData"); mOrigImageData->ReportMemory(memReporter); memReporter->EndSection(); } } DbgType* DbgModule::GetPointerType(DbgType* innerType) { auto linkedModule = GetLinkedModule(); BF_ASSERT(innerType->GetDbgModule()->GetLinkedModule() == linkedModule); if (innerType->mPtrType == NULL) { BP_ALLOC_T(DbgType); auto ptrType = mAlloc.Alloc(); ptrType->mCompileUnit = innerType->mCompileUnit; ptrType->mLanguage = innerType->mLanguage; ptrType->mTypeCode = DbgType_Ptr; ptrType->mTypeParam = innerType; ptrType->mSize = sizeof(addr_target); ptrType->mTypeIdx = (int32)linkedModule->mTypes.size(); linkedModule->mTypes.push_back(ptrType); innerType->mPtrType = ptrType; } return innerType->mPtrType; } DbgType* DbgModule::GetConstType(DbgType* innerType) { auto linkedModule = GetLinkedModule(); BF_ASSERT(innerType->GetDbgModule()->GetLinkedModule() == linkedModule); /*auto itr = linkedModule->mConstTypes.find(innerType); if (itr != linkedModule->mConstTypes.end()) return itr->second;*/ DbgType* constType = NULL; if (linkedModule->mConstTypes.TryGetValue(innerType, &constType)) return constType; BP_ALLOC_T(DbgType); constType = mAlloc.Alloc(); constType->mCompileUnit = innerType->mCompileUnit; constType->mLanguage = innerType->mLanguage; constType->mTypeCode = DbgType_Const; constType->mTypeParam = innerType; constType->mSize = sizeof(addr_target); constType->mTypeIdx = (int32)linkedModule->mTypes.size(); linkedModule->mTypes.push_back(constType); linkedModule->mConstTypes[innerType] = constType; return constType; } DbgType* DbgModule::GetPrimaryType(DbgType* dbgType) { if (dbgType->mPriority <= DbgTypePriority_Normal) { if ((dbgType->mLanguage == DbgLanguage_Beef) && (dbgType->mName != NULL)) { auto newTypeEntry = FindType(dbgType->mName, dbgType->mLanguage); if (newTypeEntry != NULL) { DbgType* newType = newTypeEntry->mValue; if ((newType->mTypeCode == DbgType_Ptr) && (newType->IsBfObjectPtr())) newType = newType->mTypeParam; newType->mPriority = DbgTypePriority_Primary_Implicit; return newType; } } else if (dbgType->mName != NULL) { auto newTypeEntry = FindType(dbgType->mName, dbgType->mLanguage); if (newTypeEntry != NULL) { DbgType* newType = newTypeEntry->mValue; newType = newType->RemoveModifiers(); if (newType != dbgType) newType = GetPrimaryType(newType); newType->mPriority = DbgTypePriority_Primary_Implicit; return newType; } } } return dbgType; } DbgType* DbgModule::GetInnerTypeOrVoid(DbgType* dbgType) { if (dbgType->mTypeParam != NULL) return dbgType->mTypeParam; return GetPrimitiveType(DbgType_Void, dbgType->mLanguage); } DbgType* DbgModule::FindTypeHelper(const String& typeName, DbgType* checkType) { for (auto subType : checkType->mSubTypeList) { if (strcmp(subType->mTypeName, typeName.c_str()) == 0) return subType; } for (auto baseType : checkType->mBaseTypes) { auto retType = FindTypeHelper(typeName, baseType->mBaseType); if (retType != NULL) return retType; } return NULL; } DbgType* DbgModule::FindType(const String& typeName, DbgType* contextType, DbgLanguage language) { if ((language == DbgLanguage_Unknown) && (contextType != NULL)) language = contextType->mLanguage; if (typeName.length() > 0) { if (typeName[typeName.length() - 1] == '*') { DbgType* dbgType = FindType(typeName.Substring(0, typeName.length() - 1), contextType, language); if (dbgType == NULL) return NULL; return GetPointerType(dbgType); } } auto entry = GetLinkedModule()->mTypeMap.Find(typeName.c_str(), language); if (entry != NULL) return entry->mValue; if (contextType != NULL) { DbgType* checkType = contextType; if (checkType->IsPointer()) checkType = checkType->mTypeParam; return FindTypeHelper(typeName, checkType); } return NULL; } DbgTypeMap::Entry* DbgModule::FindType(const char* typeName, DbgLanguage language) { return GetLinkedModule()->mTypeMap.Find(typeName, language); /*auto& typeMap = GetLinkedModule()->mTypeMap; auto dbgTypeEntry = typeMap.Find(typeName); if (dbgTypeEntry == NULL) return NULL; if (dbgTypeEntry->mValue->mLanguage == language) return dbgTypeEntry; while (dbgTypeEntry != NULL) { DbgType* dbgType = dbgTypeEntry->mValue; if ((dbgType->GetLanguage() == language) && (typeMap.StrEqual(dbgType->mName, typeName))) return dbgTypeEntry; dbgTypeEntry = dbgTypeEntry->mNext; }*/ //return NULL; } DbgType* DbgModule::GetPrimitiveType(DbgTypeCode typeCode, DbgLanguage language) { if (language == DbgLanguage_Beef) return mBfPrimitiveTypes[(int)typeCode]; else return mCPrimitiveTypes[(int)typeCode]; } DbgType* DbgModule::GetPrimitiveStructType(DbgTypeCode typeCode) { const char* name = mPrimitiveStructNames[typeCode]; if (name == NULL) return NULL; return FindType(name, NULL, DbgLanguage_Beef); } DbgType* DbgModule::GetSizedArrayType(DbgType * elementType, int count) { auto linkedModule = GetLinkedModule(); if ((linkedModule != NULL) && (linkedModule != this)) { return linkedModule->GetSizedArrayType(elementType, count); } DbgType** sizedArrayTypePtr; DbgSizedArrayEntry entry; entry.mElementType = elementType; entry.mCount = count; if (mSizedArrayTypes.TryAdd(entry, NULL, &sizedArrayTypePtr)) { BP_ALLOC_T(DbgType); auto sizedArrayType = mAlloc.Alloc(); sizedArrayType->mCompileUnit = elementType->mCompileUnit; sizedArrayType->mLanguage = elementType->mLanguage; sizedArrayType->mTypeCode = DbgType_SizedArray; sizedArrayType->mTypeParam = elementType; sizedArrayType->mSize = count * elementType->GetStride(); sizedArrayType->mAlign = elementType->GetAlign(); sizedArrayType->mSizeCalculated = true; sizedArrayType->mTypeIdx = (int32)mTypes.size(); linkedModule->mTypes.push_back(sizedArrayType); *sizedArrayTypePtr = sizedArrayType; } return *sizedArrayTypePtr; }