mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-07 19:18:19 +02:00
7663 lines
No EOL
203 KiB
C++
7663 lines
No EOL
203 KiB
C++
#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 <windows.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <inttypes.h>
|
|
#include <assert.h>
|
|
#include <vector>
|
|
#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 "BeefySysLib/util/MultiDictionary.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<DbgBlock>();
|
|
mGlobalType = mDbgModule->mAlloc.Alloc<DbgType>();
|
|
|
|
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<SubprogramRecord>();
|
|
*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<DbgSrcFile*> 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<DbgLineInfo>();
|
|
dbgSubprogram->mLineInfo->mLines.CopyFrom(&record->mLines[0], (int)record->mLines.size(), mDbgModule->mAlloc);
|
|
|
|
BfSizedArray<DbgLineInfoCtx> 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
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void DbgSubprogram::ToString(StringImpl& str, bool internalName)
|
|
{
|
|
if ((mInlineeInfo != NULL) && (mInlineeInfo->mInlineeId != 0))
|
|
mCompileUnit->mDbgModule->FixupInlinee(this);
|
|
|
|
PopulateSubprogram();
|
|
|
|
if (mCheckedKind == BfCheckedKind_Checked)
|
|
str += "[Checked] ";
|
|
else if (mCheckedKind == BfCheckedKind_Unchecked)
|
|
str += "[Unchecked] ";
|
|
|
|
auto language = GetLanguage();
|
|
if (mName == NULL)
|
|
{
|
|
if (mLinkName[0] == '<')
|
|
{
|
|
str += mLinkName;
|
|
return;
|
|
}
|
|
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) && (!internalName))
|
|
{
|
|
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)
|
|
{
|
|
mParentType->ToString(str, language, true, internalName);
|
|
if (!str.empty())
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
str += ".";
|
|
else
|
|
str += "::";
|
|
}
|
|
}
|
|
|
|
const char* name = mName;
|
|
if (mHasQualifiedName)
|
|
{
|
|
const char* cPtr = name;
|
|
for (; true; cPtr++)
|
|
{
|
|
char c = *cPtr;
|
|
if (c == 0)
|
|
break;
|
|
if ((c == ':') && (cPtr[1] == ':'))
|
|
{
|
|
name = cPtr + 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((language == DbgLanguage_Beef) && (mParentType != NULL) && (mParentType->mTypeName != NULL) && (strcmp(name, mParentType->mTypeName) == 0))
|
|
str += "this";
|
|
else if ((language == DbgLanguage_Beef) && (name[0] == '~'))
|
|
str += "~this";
|
|
else if (strncmp(name, "_bf::", 5) == 0)
|
|
str += name + 5;
|
|
else
|
|
{
|
|
bool handled = false;
|
|
if ((language == DbgLanguage_Beef) && (name[0] == '_'))
|
|
{
|
|
if (strcmp(name, "__BfCtor") == 0)
|
|
{
|
|
str += "this";
|
|
handled = true;
|
|
}
|
|
else if (strcmp(name, "__BfStaticCtor") == 0)
|
|
{
|
|
str += "this";
|
|
handled = true;
|
|
}
|
|
else if (strcmp(name, "__BfCtorClear") == 0)
|
|
{
|
|
str += "this$clear";
|
|
handled = true;
|
|
}
|
|
}
|
|
if (!handled)
|
|
str += name;
|
|
}
|
|
}
|
|
|
|
//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;
|
|
}
|
|
varType->ToString(str, language, false, internalName);
|
|
if (variable->mName != NULL)
|
|
str += " ";
|
|
}
|
|
if (variable->mName != NULL)
|
|
str += variable->mName;
|
|
showedParam = true;
|
|
i++;
|
|
}
|
|
str += ")";
|
|
}
|
|
|
|
String DbgSubprogram::ToString()
|
|
{
|
|
String str;
|
|
ToString(str, false);
|
|
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('$');
|
|
}
|
|
|
|
int DbgSubprogram::GetByteCount()
|
|
{
|
|
return (int)(mBlock.mHighPC - mBlock.mLowPC);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
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();
|
|
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--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DbgSrcFile::VerifyPath()
|
|
{
|
|
if (mVerifiedPath)
|
|
return;
|
|
|
|
if (mLineDataRefs.IsEmpty())
|
|
return;
|
|
|
|
if (!::FileExists(mFilePath))
|
|
{
|
|
bool didReplace = false;
|
|
for (auto& kv : gDebugManager->mSourcePathRemap)
|
|
{
|
|
if (mFilePath.StartsWith(kv.mKey, StringImpl::CompareKind_OrdinalIgnoreCase))
|
|
{
|
|
mFilePath.Remove(0, kv.mKey.mLength);
|
|
mFilePath.Insert(0, kv.mValue);
|
|
didReplace = true;
|
|
}
|
|
}
|
|
|
|
if (!didReplace)
|
|
{
|
|
HashSet<DbgModule*> checkedModules;
|
|
for (auto& lineDataRef : mLineDataRefs)
|
|
{
|
|
auto dbgModule = lineDataRef->mCompileUnit->mDbgModule;
|
|
if (checkedModules.Add(dbgModule))
|
|
{
|
|
if (dbgModule->mDbgFlavor == DbgFlavor_MS)
|
|
{
|
|
COFF* coff = (COFF*)dbgModule;
|
|
|
|
if ((!coff->mOrigPDBPath.IsEmpty()) && (!coff->mOrigPDBPath.Equals(coff->mPDBPath, StringImpl::CompareKind_OrdinalIgnoreCase)))
|
|
{
|
|
String relFilePath = GetRelativePath(mFilePath, coff->mOrigPDBPath);
|
|
String checkActualFilePath = GetAbsPath(relFilePath, coff->mPDBPath);
|
|
|
|
if (FileExists(checkActualFilePath))
|
|
{
|
|
mFilePath = checkActualFilePath;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mVerifiedPath = true;
|
|
}
|
|
|
|
const String& DbgSrcFile::GetLocalPath()
|
|
{
|
|
if (!mVerifiedPath)
|
|
VerifyPath();
|
|
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;
|
|
mIsDeclaration = false;
|
|
mParent = NULL;
|
|
mTypeName = NULL;
|
|
mTypeCode = DbgType_Null;
|
|
mSize = 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)
|
|
{
|
|
if ((mTypeCode == DbgType_Enum) || (dbgType->mTypeCode == DbgType_Enum))
|
|
{
|
|
// These may change mTypeCode, so redo the check afterward
|
|
GetPrimaryType();
|
|
dbgType->GetPrimaryType();
|
|
}
|
|
|
|
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()
|
|
{
|
|
PopulateType();
|
|
|
|
if (mTypeCode != DbgType_Struct)
|
|
return false;
|
|
|
|
if (mTypeParam != NULL)
|
|
return true;
|
|
|
|
auto baseType = GetBaseType();
|
|
if (baseType == NULL)
|
|
return false;
|
|
|
|
if (!baseType->IsTypedPrimitive())
|
|
return false;
|
|
|
|
mTypeParam = baseType->mTypeParam;
|
|
return true;
|
|
}
|
|
|
|
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 ((member->mName != NULL) && (strcmp(member->mName, "__bftag") == 0))
|
|
return DbgExtType_BfPayloadEnum;
|
|
}
|
|
return DbgExtType_Normal;
|
|
}
|
|
else if (strcmp(baseType->mTypeName, "ValueType") == 0)
|
|
{
|
|
for (auto member : mMemberList)
|
|
{
|
|
if ((member->mName != NULL) && (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] == ':'))
|
|
{
|
|
if (mLanguage == DbgLanguage_Beef)
|
|
{
|
|
modified = true;
|
|
inPtr++;
|
|
*(outPtr++) = '.';
|
|
if (depthCount == 0)
|
|
typeNamePtr = outPtr;
|
|
}
|
|
else if (depthCount == 0)
|
|
mTypeName = inPtr + 1;
|
|
}
|
|
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)|| (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<DbgBaseTypeEntry>();
|
|
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_%d", mCompileUnit->mDbgModule->GetLinkedModule()->mId, mTypeIdx);
|
|
return ToString(language);
|
|
}
|
|
|
|
void DbgType::ToString(StringImpl& str, DbgLanguage language, bool allowDirectBfObject, bool internalName)
|
|
{
|
|
if (language == DbgLanguage_Unknown)
|
|
language = GetLanguage();
|
|
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
switch (mTypeCode)
|
|
{
|
|
case DbgType_UChar:
|
|
str += "char8";
|
|
return;
|
|
case DbgType_UChar16:
|
|
str += "char16";
|
|
return;
|
|
case DbgType_UChar32:
|
|
str += "char32";
|
|
return;
|
|
case DbgType_i8:
|
|
str += "int8";
|
|
return;
|
|
case DbgType_u8:
|
|
str += "uint8";
|
|
return;
|
|
case DbgType_i16:
|
|
str += "int16";
|
|
return;
|
|
case DbgType_u16:
|
|
str += "uint16";
|
|
return;
|
|
case DbgType_i32:
|
|
str += "int32";
|
|
return;
|
|
case DbgType_u32:
|
|
str += "uint32";
|
|
return;
|
|
case DbgType_i64:
|
|
str += "int64";
|
|
return;
|
|
case DbgType_u64:
|
|
str += "uint64";
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (mTypeCode)
|
|
{
|
|
case DbgType_SChar:
|
|
str += "char";
|
|
return;
|
|
case DbgType_SChar16:
|
|
str += "wchar_t";
|
|
return;
|
|
case DbgType_SChar32:
|
|
str += "int32_t";
|
|
return;
|
|
case DbgType_UChar:
|
|
str += "uint8_t";
|
|
return;
|
|
case DbgType_UChar16:
|
|
str += "uint16_t";
|
|
return;
|
|
case DbgType_UChar32:
|
|
str += "uint32_t";
|
|
return;
|
|
case DbgType_i8:
|
|
str += "char";
|
|
return;
|
|
case DbgType_u8:
|
|
str += "uint8_t";
|
|
return;
|
|
case DbgType_i16:
|
|
str += "short";
|
|
return;
|
|
case DbgType_u16:
|
|
str += "uint16_t";
|
|
return;
|
|
case DbgType_i32:
|
|
str += "int";
|
|
return;
|
|
case DbgType_u32:
|
|
str += "uint32_t";
|
|
return;
|
|
case DbgType_i64:
|
|
str += "int64_t";
|
|
return;
|
|
case DbgType_u64:
|
|
str += "uint64_t";
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mTypeCode == DbgType_Namespace)
|
|
internalName = false;
|
|
|
|
auto parent = mParent;
|
|
if ((parent == NULL) && (internalName))
|
|
{
|
|
auto primaryType = GetPrimaryType();
|
|
parent = primaryType->mParent;
|
|
}
|
|
|
|
if (mTypeName != NULL)
|
|
{
|
|
if ((!allowDirectBfObject) && (IsBfObject()))
|
|
{
|
|
// Only use the '#' for testing
|
|
//return ToString(true) + "#";
|
|
ToString(str, DbgLanguage_Unknown, true, internalName);
|
|
return;
|
|
}
|
|
|
|
if (IsGlobalsContainer())
|
|
{
|
|
if (mParent != NULL)
|
|
{
|
|
mParent->ToString(str, language, false, internalName);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//String combName;
|
|
/*if (mTemplateParams != NULL)
|
|
{
|
|
combName = nameP;
|
|
combName += mTemplateParams;
|
|
nameP = combName.c_str();
|
|
}*/
|
|
|
|
if ((!mFixedName) /*&& (language == DbgLanguage_Beef)*/)
|
|
{
|
|
FixName();
|
|
}
|
|
char* nameP = (char*)mTypeName;
|
|
|
|
if (parent == NULL)
|
|
{
|
|
if (strncmp(nameP, "Box<", 4) == 0)
|
|
{
|
|
str += String(nameP + 4, nameP + strlen(nameP) - 1);
|
|
str += "^";
|
|
return;
|
|
}
|
|
|
|
// For declarations, may also include namespaces
|
|
str += mName;
|
|
return;
|
|
}
|
|
|
|
if (GetLanguage() == DbgLanguage_Beef)
|
|
{
|
|
parent->ToString(str, language, allowDirectBfObject, internalName);
|
|
if ((internalName) && (parent->mTypeCode != DbgType_Namespace))
|
|
str += "+";
|
|
else
|
|
str += ".";
|
|
str += nameP;
|
|
}
|
|
else
|
|
{
|
|
parent->ToString(str, language, allowDirectBfObject, internalName);
|
|
if ((internalName) && (parent->mTypeCode != DbgType_Namespace))
|
|
str += "+";
|
|
else
|
|
str += "::";
|
|
str += nameP;
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (mTypeCode)
|
|
{
|
|
case DbgType_Struct:
|
|
{
|
|
if ((mTypeName == NULL) && (parent != NULL))
|
|
{
|
|
parent->ToString(str, language, allowDirectBfObject, internalName);
|
|
return;
|
|
}
|
|
str += "@struct";
|
|
return;
|
|
}
|
|
case DbgType_Class:
|
|
{
|
|
str += "@class";
|
|
return;
|
|
}
|
|
case DbgType_TypeDef:
|
|
{
|
|
str += "@typedef";
|
|
return;
|
|
}
|
|
case DbgType_Const:
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
str += "readonly";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
return;
|
|
}
|
|
|
|
str += "const";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
return;
|
|
}
|
|
case DbgType_Volatile:
|
|
{
|
|
str += "volatile";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
return;
|
|
}
|
|
case DbgType_Unaligned:
|
|
{
|
|
str += "unaligned";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
}
|
|
case DbgType_Restrict:
|
|
{
|
|
str += "restrict";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
}
|
|
case DbgType_Ptr:
|
|
{
|
|
if (mTypeParam == NULL)
|
|
{
|
|
str += "void*";
|
|
return;
|
|
}
|
|
|
|
if (mTypeParam->IsBfObject())
|
|
{
|
|
mTypeParam->ToString(str, DbgLanguage_Unknown, true, internalName);
|
|
return;
|
|
}
|
|
|
|
// Don't put a "*" on the end of a function type, it's implicit
|
|
if (mTypeParam->mTypeCode == DbgType_Subroutine)
|
|
{
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
return;
|
|
}
|
|
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += "*";
|
|
return;
|
|
}
|
|
case DbgType_Ref:
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
str += "ref";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
return;
|
|
}
|
|
if (mTypeParam == NULL)
|
|
{
|
|
str += "&";
|
|
return;
|
|
}
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += "&";
|
|
return;
|
|
}
|
|
case DbgType_RValueReference:
|
|
{
|
|
if (language == DbgLanguage_Beef)
|
|
{
|
|
// Ignore this - this is used for passing structs when we're not using the 'byval' attribute
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
return;
|
|
}
|
|
|
|
if (mTypeParam == NULL)
|
|
{
|
|
str += "&&";
|
|
return;
|
|
}
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += "&&";
|
|
return;
|
|
}
|
|
case DbgType_Unspecified:
|
|
str += mTypeName;
|
|
return;
|
|
case DbgType_SizedArray:
|
|
{
|
|
StringT<128> 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;
|
|
}
|
|
checkType->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += name;
|
|
return;
|
|
}
|
|
case DbgType_Union:
|
|
{
|
|
str += "union";
|
|
if (mTypeParam != NULL)
|
|
{
|
|
str += " ";
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
}
|
|
return;
|
|
}
|
|
case DbgType_Single:
|
|
str += "float";
|
|
return;
|
|
case DbgType_Double:
|
|
str += "double";
|
|
return;
|
|
case DbgType_Null:
|
|
str += "void";
|
|
return;
|
|
case DbgType_Subroutine:
|
|
{
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += " (";
|
|
int paramIdx = 0;
|
|
for (auto param : mBlockParam->mVariables)
|
|
{
|
|
if (paramIdx > 0)
|
|
str += ", ";
|
|
param->mType->ToString(str, language, allowDirectBfObject, internalName);
|
|
paramIdx++;
|
|
}
|
|
str += ")";
|
|
return;
|
|
}
|
|
case DbgType_VTable:
|
|
str += "@vtable";
|
|
return;
|
|
case DbgType_Enum:
|
|
str += "@enum";
|
|
return;
|
|
case DbgType_Namespace:
|
|
{
|
|
// Anonymous
|
|
str += "`anon`";
|
|
return;
|
|
}
|
|
case DbgType_PtrToMember:
|
|
str += "@ptrToMember";
|
|
return;
|
|
case DbgType_Bitfield:
|
|
{
|
|
auto dbgBitfieldType = (DbgBitfieldType*)this;
|
|
mTypeParam->ToString(str, language, allowDirectBfObject, internalName);
|
|
str += StrFormat("{%d:%d}", dbgBitfieldType->mPosition, dbgBitfieldType->mLength);
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
BF_FATAL("Unhandled type");
|
|
str += "???";
|
|
}
|
|
|
|
String DbgType::ToString(DbgLanguage language, bool allowDirectBfObject)
|
|
{
|
|
String str;
|
|
ToString(str, language, allowDirectBfObject, false);
|
|
return str;
|
|
}
|
|
|
|
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>(); \
|
|
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>(); \
|
|
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>(); \
|
|
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_i16, "int16_t", int16_t);
|
|
CREATE_PRIMITIVE_C(DbgType_i32, "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;
|
|
mId = 0;
|
|
mStartSubprogramIdx = 0;
|
|
mEndSubprogramIdx = 0;
|
|
mCodeAddress = NULL;
|
|
mMayBeOld = false;
|
|
mTimeStamp = 0;
|
|
mExpectedFileSize = 0;
|
|
mBfTypeType = NULL;
|
|
mBfTypesInfoAddr = 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;
|
|
}
|
|
|
|
String errorStr = "error ";
|
|
if (!mFilePath.IsEmpty())
|
|
{
|
|
errorStr += "Error in ";
|
|
errorStr += mFilePath;
|
|
errorStr += ": ";
|
|
}
|
|
errorStr += error;
|
|
errorStr += "\n";
|
|
|
|
mDebugger->OutputRawMessage(errorStr);
|
|
mFailed = true;
|
|
}
|
|
|
|
void DbgModule::SoftFail(const StringImpl& error)
|
|
{
|
|
if (mFailMsgPtr != NULL)
|
|
{
|
|
if (mFailMsgPtr->IsEmpty())
|
|
*mFailMsgPtr = error;
|
|
}
|
|
|
|
String errorStr = "errorsoft ";
|
|
if (!mFilePath.IsEmpty())
|
|
{
|
|
errorStr += "Error in ";
|
|
errorStr += mFilePath;
|
|
errorStr += ": ";
|
|
}
|
|
errorStr += error;
|
|
errorStr += "\n";
|
|
|
|
mDebugger->OutputRawMessage(errorStr);
|
|
mFailed = true;
|
|
}
|
|
|
|
void DbgModule::HardFail(const StringImpl& error)
|
|
{
|
|
if (mFailMsgPtr != NULL)
|
|
{
|
|
if (mFailMsgPtr->IsEmpty())
|
|
*mFailMsgPtr = error;
|
|
}
|
|
|
|
String errorStr;
|
|
if (!mFilePath.IsEmpty())
|
|
{
|
|
errorStr += "Error in ";
|
|
errorStr += mFilePath;
|
|
errorStr += ": ";
|
|
}
|
|
errorStr += error;
|
|
errorStr += "\n";
|
|
|
|
BF_FATAL(errorStr.c_str());
|
|
}
|
|
|
|
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<DbgExceptionDirectoryEntry>();
|
|
|
|
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 <typename T> static bool IsTypeSigned() { return false; }
|
|
template <> bool IsTypeSigned<int8>() { return true; }
|
|
template <> bool IsTypeSigned<int16>() { return true; }
|
|
template <> bool IsTypeSigned<int32>() { return true; }
|
|
template <> bool IsTypeSigned<int64>() { 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 <typename T>
|
|
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<T>())
|
|
return (T)(intptr)GET(int8);
|
|
else
|
|
return (T)(uintptr)GET(uint8);
|
|
}
|
|
break;
|
|
case DW_FORM_data2:
|
|
{
|
|
if (IsTypeSigned<T>())
|
|
return (T)(intptr)GET(int16);
|
|
else
|
|
return (T)(uintptr)GET(uint16);
|
|
}
|
|
break;
|
|
case DW_FORM_data4:
|
|
{
|
|
if (IsTypeSigned<T>())
|
|
return (T)(intptr)GET(int32);
|
|
else
|
|
return (T)(uintptr)GET(uint32);
|
|
}
|
|
break;
|
|
case DW_FORM_data8:
|
|
{
|
|
if (IsTypeSigned<T>())
|
|
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<DbgType*>(typeIdx);
|
|
if (dbgType != NULL)
|
|
return dbgType;
|
|
dbgType = mAlloc.Alloc<DbgType>();
|
|
dbgType->mTypeIdx = (int)linkedModule->mTypes.size();
|
|
linkedModule->mTypes.push_back(dbgType);
|
|
dataMap.Set(typeIdx, dbgType);
|
|
return dbgType;
|
|
}
|
|
|
|
typedef std::pair<DbgClassType, void*> DataPair;
|
|
typedef llvm::SmallVector<DataPair, 16> DataStack;
|
|
|
|
template <typename T>
|
|
T DbgModule::GetOrCreate(int idx, DbgDataMap& dataMap)
|
|
{
|
|
if (idx == 0)
|
|
return NULL;
|
|
T val = dataMap.Get<T>(idx);
|
|
if (val != NULL)
|
|
return val;
|
|
val = mAlloc.Alloc<typename RemoveTypePointer<T>::type >();
|
|
dataMap.Set(idx, val);
|
|
return val;
|
|
}
|
|
|
|
template <typename T>
|
|
static T GetStackTop(DataStack* dataStack)
|
|
{
|
|
auto dataPair = dataStack->back();
|
|
if (dataPair.first == RemoveTypePointer<T>::type::ClassType)
|
|
return (T)dataPair.second;
|
|
return NULL;
|
|
}
|
|
|
|
template <>
|
|
DbgBlock* GetStackTop<DbgBlock*>(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 <typename T>
|
|
static bool StackHasType(DataStack* dataStack)
|
|
{
|
|
for (auto itr : *dataStack)
|
|
if (itr.first == RemoveTypePointer<T>::type::ClassType)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
template <typename T>
|
|
static T GetStackLast(DataStack* dataStack)
|
|
{
|
|
for (int i = (int)dataStack->size() - 1; i >= 0; i--)
|
|
{
|
|
if ((*dataStack)[i].first == RemoveTypePointer<T>::type::ClassType)
|
|
return (T)(*dataStack)[i].second;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
template <typename T>
|
|
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 '<unnamed-type-u>'
|
|
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->mDefinedMembersSize++;
|
|
}
|
|
|
|
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->mDefinedMembersSize > 0)
|
|
{
|
|
if (dbgType->mDefinedMembersSize > 0)
|
|
{
|
|
// We create an 'alternates' list for all types that define at least one static field
|
|
if (prevType->mHasStaticMembers)
|
|
prevType->mAlternates.PushFront(dbgType, &mAlloc);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// if (prevType->mDefinedMembersSize > dbgType->mDefinedMembersSize)
|
|
// {
|
|
// continue;
|
|
// }
|
|
|
|
if (prevType->mMethodsWithParamsCount > dbgType->mMethodsWithParamsCount)
|
|
{
|
|
// This handles a special case where methods without line data like <Enum>.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<DbgType>();
|
|
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<DbgSubprogramMapEntry>();
|
|
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<int, DbgType*> typeMap;
|
|
//std::map<int, DbgSubprogram*> subprogramMap;
|
|
int tagStart = (int)(data - startData);
|
|
int tagEnd = (int)(dataEnd - startData);
|
|
DbgDataMap dataMap(tagStart, tagEnd);
|
|
DataStack dataStack;
|
|
Array<AbstractOriginEntry> abstractOriginReplaceList;
|
|
|
|
Array<int> 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<DbgType*>(&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<DbgType>();
|
|
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<char>(data, form);
|
|
break;
|
|
case DW_AT_location:
|
|
atLocationLen = (int)ReadValue<uint>(data, form, dataOfs, &atLocationData, startData);
|
|
break;
|
|
case DW_AT_name:
|
|
atName = ReadValue<const char*>(data, form);
|
|
break;
|
|
case DW_AT_ordering:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_byte_size:
|
|
atByteSize = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_bit_offset:
|
|
atBitOffset = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_bit_size:
|
|
atBitSize = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_stmt_list:
|
|
ReadValue<int64_t>(data, form);
|
|
break;
|
|
case DW_AT_low_pc:
|
|
atLowPC = RemapAddr((addr_target)ReadValue<int64_t>(data, form));
|
|
break;
|
|
case DW_AT_high_pc:
|
|
atHighPC = ReadValue<int64_t>(data, form);
|
|
break;
|
|
case DW_AT_language:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_discr:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_discr_value:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_visibility:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_import:
|
|
atImport = ReadValue<int>(data, form) + dataOfs;
|
|
break;
|
|
case DW_AT_string_length:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_common_reference:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_comp_dir:
|
|
atCompDir = ReadValue<const char*>(data, form);
|
|
break;
|
|
case DW_AT_const_value:
|
|
atConstValue = ReadValue<int64>(data, form);
|
|
hadConstValue = true;
|
|
break;
|
|
case DW_AT_containing_type:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_default_value:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_inline:
|
|
atInline = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_is_optional:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_lower_bound:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_producer:
|
|
atProducer = ReadValue<const char*>(data, form);
|
|
break;
|
|
case DW_AT_prototyped:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_return_addr:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_start_scope:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_bit_stride:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_upper_bound:
|
|
// Lower bound not supported
|
|
atCount = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_abstract_origin:
|
|
atAbstractOrigin = ReadValue<int>(data, form, dataOfs);
|
|
break;
|
|
case DW_AT_accessibility:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_address_class:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_artificial:
|
|
atArtificial = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_base_types:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_calling_convention:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_count:
|
|
atCount = ReadValue<uint>(data, form);
|
|
break;
|
|
case DW_AT_data_member_location:
|
|
if (form == DW_FORM_exprloc)
|
|
{
|
|
atDataMemberLocation = (int)ReadValue<uint>(data, form, dataOfs, &atDataMemberData);
|
|
hadMemberLocation = true;
|
|
}
|
|
else
|
|
{
|
|
atDataMemberLocation = (int)ReadValue<uint>(data, form);
|
|
hadMemberLocation = true;
|
|
}
|
|
break;
|
|
case DW_AT_decl_column:
|
|
/*TODO:*/ ReadValue<uint32>(data, form);
|
|
break;
|
|
case DW_AT_decl_file:
|
|
atDeclFile = ReadValue<uint32>(data, form);
|
|
break;
|
|
case DW_AT_decl_line:
|
|
atDeclLine = ReadValue<uint32>(data, form);
|
|
break;
|
|
case DW_AT_declaration:
|
|
atDeclaration = ReadValue<bool>(data, form);
|
|
break;
|
|
case DW_AT_discr_list:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_encoding:
|
|
atEncoding = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_external:
|
|
atExternal = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_frame_base:
|
|
atFrameBaseLength = (int64_t)ReadValue<uint64_t>(data, form, dataOfs, &atFrameBase);
|
|
break;
|
|
case DW_AT_friend:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_identifier_case:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_macro_info:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_namelist_item:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_priority:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_segment:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_specification:
|
|
atSpecification = ReadValue<int>(data, form, dataOfs);
|
|
break;
|
|
case DW_AT_static_link:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_type:
|
|
atType = ReadValue<int>(data, form, dataOfs);
|
|
break;
|
|
case DW_AT_use_location:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_variable_parameter:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_virtuality:
|
|
atVirtual = ReadValue<int>(data, form) != 0;
|
|
break;
|
|
case DW_AT_vtable_elem_location:
|
|
ReadValue<uint64_t>(data, form, dataOfs, &atVirtualLocData);
|
|
break;
|
|
case DW_AT_allocated:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_associated:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_data_location:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_byte_stride:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_entry_pc:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_use_UTF8:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_extension:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_ranges:
|
|
atRanges = (int)ReadValue<uint>(data, form);
|
|
hasRanges = true;
|
|
break;
|
|
case DW_AT_trampoline:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_call_column:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_call_file:
|
|
atCallFile = ReadValue<uint32>(data, form);
|
|
break;
|
|
case DW_AT_call_line:
|
|
atCallLine = ReadValue<uint32>(data, form);
|
|
break;
|
|
case DW_AT_description:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_binary_scale:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_decimal_scale:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_small:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_decimal_sign:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_digit_count:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_picture_string:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_mutable:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_threads_scaled:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_explicit:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_object_pointer:
|
|
atObjectPointer = ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_endianity:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_elemental:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_pure:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_recursive:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_signature:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_main_subprogram:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_data_bit_offset:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_const_expr:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_enum_class:
|
|
/*TODO:*/ ReadValue<int>(data, form);
|
|
break;
|
|
case DW_AT_linkage_name:
|
|
atLinkageName = ReadValue<const char*>(data, form);
|
|
break;
|
|
|
|
//
|
|
case DW_AT_MIPS_linkage_name:
|
|
atLinkageName = ReadValue<const char*>(data, form);
|
|
break;
|
|
|
|
case DW_AT_APPLE_optimized:
|
|
isOptimized = ReadValue<bool>(data, form);
|
|
break;
|
|
|
|
default:
|
|
ReadValue<int>(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<DbgType*>(&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<DbgSubprogram*>(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<DbgSubprogram*>(atAbstractOrigin, dataMap);
|
|
auto abstractOriginEntry = AbstractOriginEntry::Create(DbgSubprogram::ClassType, subProgram, originSubProgram);
|
|
abstractOriginReplaceList.push_back(abstractOriginEntry);
|
|
}
|
|
subProgram->mParentType = GetStackTop<DbgType*>(&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<DbgSubprogram*>(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<DbgSubprogram*>(&dataStack);
|
|
subProgram->mInlineeInfo = mAlloc.Alloc<DbgInlineeInfo>();
|
|
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<DbgBlock*>(&dataStack);
|
|
DbgBlock* dwBlock = mAlloc.Alloc<DbgBlock>();
|
|
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<DbgBlock*>(&dataStack);
|
|
|
|
if (atName && !strncmp(atName, "__asmLines", 10))
|
|
{
|
|
const char* ptr = strchr(atName, '.');
|
|
if (!ptr)
|
|
break;
|
|
|
|
int declLine = atDeclLine;
|
|
Array<int> 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; iLine<curRunCount; ++iLine)
|
|
{
|
|
curAsmLine += decodedValue;
|
|
asmLines.push_back(curAsmLine);
|
|
}
|
|
}
|
|
parity = !parity;
|
|
}
|
|
BF_ASSERT(!parity);
|
|
|
|
if (dwBlock->mAsmDebugLineMap == 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<DbgVariable*>(atSpecification);
|
|
//BF_ASSERT(dbgVariable != NULL);
|
|
|
|
dbgVariable = GetOrCreate<DbgVariable*>(atSpecification, dataMap);
|
|
//dbgVariable = dataMap.Get<DbgVariable*>(atSpecification);
|
|
//BF_ASSERT(dbgVariable != NULL);
|
|
}
|
|
else if (dwBlock != NULL)
|
|
{
|
|
dbgVariable = GetOrCreate<DbgVariable*>(tagIdx, dataMap);
|
|
dwBlock->mVariables.PushBack(dbgVariable);
|
|
}
|
|
else
|
|
{
|
|
DbgType* dbgType = GetStackTop<DbgType*>(&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<DbgCompileUnit*>(&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<DbgVariable*>(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<DbgVariable*>(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<DbgSubprogram*>(&dataStack);
|
|
|
|
if (dwSubprogram == NULL)
|
|
{
|
|
if ((atName == NULL) && (atAbstractOrigin == 0))
|
|
{
|
|
DbgType* dbgType = GetStackTop<DbgType*>(&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>();
|
|
DbgVariable* dwVariable = GetOrCreate<DbgVariable*>(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<DbgVariable>();
|
|
member->mCompileUnit = compileUnit;
|
|
member->mConstValue = atConstValue;
|
|
member->mName = atName;
|
|
member->mIsStatic = true;
|
|
member->mIsConst = true;
|
|
|
|
DbgType* parentType = GetStackTop<DbgType*>(&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<DbgBlock*>(&dataStack);
|
|
dataStack.push_back(prevTop);
|
|
|
|
if (dwBlock != NULL)
|
|
{
|
|
DbgVariable* dwVariable = mAlloc.Alloc<DbgVariable>();
|
|
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<DbgType*>(&dataStack);
|
|
parentType->mArraySize = atUpperBound;
|
|
}
|
|
break;*/
|
|
case DW_TAG_inheritance:
|
|
{
|
|
DbgType* derivedType = GetStackTop<DbgType*>(&dataStack);
|
|
DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc<DbgBaseTypeEntry>();
|
|
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<DbgType*>(&dataStack);
|
|
|
|
if ((atName != NULL) && (strncmp(atName, "_vptr$", 6) == 0))
|
|
{
|
|
parentType->mHasVTable = true;
|
|
break;
|
|
}
|
|
//DbgVariable* member = mAlloc.Alloc<DbgVariable>();
|
|
DbgVariable* member = GetOrCreate<DbgVariable*>(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<DbgType*>(&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<DbgType*>(&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<DbgType>();
|
|
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<DbgBlock>();
|
|
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<addr_target, DwCommonFrameDescriptor*> 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<DwCommonFrameDescriptor>();
|
|
|
|
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<addr_target, DwCommonFrameDescriptor*> 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<DwCommonFrameDescriptor>();
|
|
|
|
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<DbgLineData>& 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<const char*> directoryNames;
|
|
while (true)
|
|
{
|
|
const char* name = DataGetString(data);
|
|
if (name[0] == 0)
|
|
break;
|
|
directoryNames.push_back(name);
|
|
}
|
|
|
|
DbgSrcFileReference* dwSrcFileRef = NULL;
|
|
|
|
HashSet<String> 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;
|
|
|
|
BfLogDbg("DbgModule::GetHotTargetAddress %p %p\n", this, hotTargetSection);
|
|
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];
|
|
|
|
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()
|
|
{
|
|
BfLogDbg("DbgModule::CommitHotTargetSections %p\n", this);
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DbgModule::HasHotReplacedMethods(DbgType* type)
|
|
{
|
|
for (auto newMethod : type->mMethodList)
|
|
{
|
|
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType)
|
|
{
|
|
auto linkedModule = GetLinkedModule();
|
|
|
|
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<DbgSrcFile*> checkedFiles;
|
|
//for (auto method : primaryType->mMethodList)
|
|
|
|
auto _RemoveLineInfo = [&](DbgSubprogram* method)
|
|
{
|
|
//method->mWasModuleHotReplaced = true;
|
|
method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
|
|
|
|
if (method->mLineInfo == NULL)
|
|
return;
|
|
|
|
//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();
|
|
|
|
MultiDictionary<StringView, DbgSubprogram*> oldProgramMap;
|
|
auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod)
|
|
{
|
|
oldMethod->PopulateSubprogram();
|
|
if (oldMethod->mBlock.IsEmpty())
|
|
return;
|
|
auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
|
|
if (symInfo != NULL)
|
|
{
|
|
StringView key = StringView(symInfo->mName);
|
|
DbgSubprogram** oldestValuePtr = NULL;
|
|
int count = 0;
|
|
for (auto itr = oldProgramMap.TryGet(key); itr != oldProgramMap.end(); ++itr)
|
|
{
|
|
auto& checkProgram = itr.GetValue();
|
|
if ((oldestValuePtr == NULL) ||
|
|
(checkProgram->mCompileUnit->mDbgModule->mHotIdx < (*oldestValuePtr)->mCompileUnit->mDbgModule->mHotIdx))
|
|
oldestValuePtr = &checkProgram;
|
|
count++;
|
|
}
|
|
|
|
// If we hot jump from only the last version of the method then we potentially create a long "jump chain"
|
|
// from a call to the original method through every hot version to this current one. We also don't want to
|
|
// blindly update every old version because that will cause hot loading to become too slow over many iterations.
|
|
// We balance this by linking SOME old versions to the newest one, decreasing in frequency as we move through
|
|
// the older methods
|
|
if ((count == 0) ||
|
|
(mDebugTarget->mHotChainBreakIdx++) % (1 << (count + 1)) == 0)
|
|
{
|
|
oldProgramMap.Add(StringView(symInfo->mName), oldMethod);
|
|
}
|
|
else if ((count > 1) && ((mDebugTarget->mHotChainBreakIdx++) % 2 == 0))
|
|
{
|
|
// Prefer the older of the methods when we are not adding a new entry. This will also ensure that the
|
|
// original method gets updated very often
|
|
*oldestValuePtr = oldMethod;
|
|
}
|
|
}
|
|
};
|
|
|
|
if (newType != primaryType)
|
|
{
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
for (auto oldMethod : primaryType->mHotReplacedMethodList)
|
|
{
|
|
_AddToHotReplacedMethodList(oldMethod);
|
|
}
|
|
|
|
bool setHotJumpFailed = false;
|
|
|
|
auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod)
|
|
{
|
|
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)))
|
|
{
|
|
_RemoveLineInfo(oldMethod);
|
|
}
|
|
else
|
|
setHotJumpFailed = true;
|
|
}
|
|
oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
|
|
}
|
|
};
|
|
|
|
auto _ReplaceMethod = [&](DbgSubprogram* newMethod)
|
|
{
|
|
bool didReplace = false;
|
|
if (!newMethod->mBlock.IsEmpty())
|
|
{
|
|
newMethod->PopulateSubprogram();
|
|
|
|
auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
|
|
BfLogDbg("Hot added new method %p %s Address:%p Sym:%s\n",
|
|
newMethod, newMethod->mName, newMethod->mBlock.mLowPC,
|
|
(symInfo != NULL) ? symInfo->mName : "???");
|
|
if (symInfo != NULL)
|
|
{
|
|
DbgSubprogram* oldMethod = NULL;
|
|
for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr)
|
|
{
|
|
auto oldMethod = itr.GetValue();
|
|
_HotJump(oldMethod, newMethod);
|
|
didReplace = true;
|
|
}
|
|
}
|
|
}
|
|
return didReplace;
|
|
};
|
|
|
|
if (newType == primaryType)
|
|
{
|
|
// In-place method swapping
|
|
|
|
auto newMethod = newType->mMethodList.front();
|
|
while (newMethod != NULL)
|
|
{
|
|
if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
|
|
{
|
|
// Our object file contains this function
|
|
if (_ReplaceMethod(newMethod))
|
|
{
|
|
auto nextMethod = newMethod->mNext;
|
|
newType->mMethodList.Remove(newMethod);
|
|
primaryType->mHotReplacedMethodList.PushFront(newMethod);
|
|
newMethod = nextMethod;
|
|
}
|
|
else
|
|
newMethod = newMethod->mNext;
|
|
}
|
|
else
|
|
{
|
|
_AddToHotReplacedMethodList(newMethod);
|
|
newMethod = newMethod->mNext;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (!newType->mMethodList.IsEmpty())
|
|
{
|
|
DbgSubprogram* newMethod = newType->mMethodList.PopFront();
|
|
_ReplaceMethod(newMethod);
|
|
newMethod->mParentType = primaryType;
|
|
primaryType->mMethodList.PushBack(newMethod);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
HotReplaceMethods(newType, primaryType);
|
|
|
|
//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;
|
|
}
|
|
|
|
const char* DbgModule::GetStringTable(DataStream* stream, int stringTablePos)
|
|
{
|
|
if (mStringTable == NULL)
|
|
{
|
|
int prevPos = stream->GetPos();
|
|
stream->SetPos(stringTablePos);
|
|
int strTableSize = 0;
|
|
stream->Read(&strTableSize, 4);
|
|
if (strTableSize != 0)
|
|
{
|
|
strTableSize -= 4;
|
|
|
|
char* strTableData = new char[strTableSize + 4];
|
|
memcpy(strTableData, &strTableSize, 4);
|
|
stream->Read(strTableData + 4, strTableSize);
|
|
mStringTable = strTableData;
|
|
}
|
|
|
|
stream->SetPos(prevPos);
|
|
}
|
|
return mStringTable;
|
|
}
|
|
|
|
bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind)
|
|
{
|
|
BP_ZONE("DbgModule::ReadCOFF");
|
|
|
|
//if (this == mDebugTarget->mTargetBinary)
|
|
//mMemReporter = new MemReporter();
|
|
|
|
BfLogDbg("DbgModule::ReadCOFF %p %s\n", this, mFilePath.c_str());
|
|
|
|
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);
|
|
}
|
|
|
|
bool wantStringTable = isObjectFile;
|
|
|
|
stream->SetPos(sectionStartPos);
|
|
for (int dirNum = 0; dirNum < (int) ntHdr.mFileHeader.mNumberOfSections; dirNum++)
|
|
{
|
|
PESectionHeader sectHdr;
|
|
|
|
char* name = sectHdr.mName;
|
|
stream->Read(§Hdr, sizeof(PESectionHeader));
|
|
if (sectHdr.mSizeOfRawData > 0)
|
|
sectionDataEndPos = BF_MAX(sectionDataEndPos, (int)(sectHdr.mPointerToRawData + sectHdr.mSizeOfRawData));
|
|
if (sectHdr.mNumberOfRelocations > 0)
|
|
sectionDataEndPos = BF_MAX(sectionDataEndPos, (int)(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 stringTablePos = curPos;
|
|
if (isObjectFile)
|
|
GetStringTable(stream, stringTablePos);
|
|
|
|
int mDebugFrameDataLen = 0;
|
|
|
|
stream->SetPos(sectionStartPos);
|
|
|
|
PEDataDirectory* exportDataDir = &ntHdr.mOptionalHeader.mDataDirectory[0];
|
|
|
|
mHotTargetSections.Resize(ntHdr.mFileHeader.mNumberOfSections);
|
|
|
|
Array<PESectionHeader> sectionHeaders;
|
|
sectionHeaders.Resize(ntHdr.mFileHeader.mNumberOfSections);
|
|
mSectionHeaders.Resize(sectionHeaders.size() + 1);
|
|
|
|
Array<String> sectionNames;
|
|
sectionNames.Resize(ntHdr.mFileHeader.mNumberOfSections);
|
|
|
|
stream->Read(§ionHeaders[0], sizeof(PESectionHeader) * ntHdr.mFileHeader.mNumberOfSections);
|
|
|
|
for (int sectNum = 0; sectNum < ntHdr.mFileHeader.mNumberOfSections; sectNum++)
|
|
{
|
|
mSectionHeaders[sectNum] = sectionHeaders[sectNum];
|
|
}
|
|
|
|
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));
|
|
|
|
const char* name = sectHdr.mName;
|
|
if (name[0] == '/')
|
|
{
|
|
int strIdx = atoi(name + 1);
|
|
name = &GetStringTable(stream, stringTablePos)[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.mName = name;
|
|
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<DbgSymbol>();
|
|
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);
|
|
}
|
|
|
|
// Old, unsupported DWARF debug info
|
|
/*
|
|
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 = (char*)GetStringTable(stream, stringTablePos) + 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 = mSectionHeaders[symInfo->mSectionNum - 1].mVirtualAddress + 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<DbgSymbol>();
|
|
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<DbgSymbol>();
|
|
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->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType))
|
|
{
|
|
HotReplaceMethods(dbgType, primaryType);
|
|
}
|
|
|
|
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 >= (int)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<DbgSymbol*> 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("<StartData>", startingOffset);
|
|
}
|
|
lastSym = dbgSym;
|
|
}
|
|
|
|
if (lastSym != NULL)
|
|
mMemReporter->Add(lastSym->mName, (int)((mImageBase + sectHdr.mVirtualAddress + sectHdr.mSizeOfRawData) - lastSym->mAddress));
|
|
else
|
|
{
|
|
mMemReporter->Add("<Unaccounted>", (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<String> 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_target>(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);
|
|
intptr 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<int>(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];
|
|
}
|
|
|
|
intptr 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 (path.StartsWith("$Emit"))
|
|
return DbgFileExistKind_Found;
|
|
|
|
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 <typename TRadixMap>
|
|
static void RemoveInvalidRange(TRadixMap& radixMap, addr_target startAddr, int addrLength)
|
|
{
|
|
radixMap.RemoveRange(startAddr, addrLength);
|
|
}
|
|
|
|
template <typename TMap>
|
|
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<DbgSrcFile*> 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<DbgType*>));
|
|
memReporter->AddHashSet("TypeMap", mTypeMap.mMap);
|
|
memReporter->Add("SymbolNameMap", mSymbolNameMap.mAlloc.GetTotalAllocSize() + sizeof(StrHashMap<DbgType*>));
|
|
|
|
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<DbgType>();
|
|
ptrType->mCompileUnit = innerType->mCompileUnit;
|
|
ptrType->mLanguage = innerType->mLanguage;
|
|
ptrType->mTypeCode = DbgType_Ptr;
|
|
ptrType->mTypeParam = innerType;
|
|
ptrType->mSize = sizeof(addr_target);
|
|
ptrType->mAlign = (int)ptrType->mSize;
|
|
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<DbgType>();
|
|
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, bool bfObjectPtr)
|
|
{
|
|
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, bfObjectPtr);
|
|
if (dbgType == NULL)
|
|
return NULL;
|
|
return GetPointerType(dbgType);
|
|
}
|
|
}
|
|
|
|
auto entry = GetLinkedModule()->mTypeMap.Find(typeName.c_str(), language);
|
|
if (entry != NULL)
|
|
{
|
|
if ((bfObjectPtr) && (entry->mValue->IsBfObject()))
|
|
return GetPointerType(entry->mValue);
|
|
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<DbgType>();
|
|
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;
|
|
} |