#include "COFF.h" #include "BeefySysLib/FileStream.h" #include "BeefySysLib/MemStream.h" #include "codeview/cvinfo.h" #include "DebugTarget.h" #include "DebugManager.h" #include "DWARFInfo.h" #include "BeefySysLib/Util/PerfTimer.h" #include "BeefySysLib/Util/Dictionary.h" #include "BeefySysLib/Util/BeefPerf.h" #include "BeefySysLib/platform/PlatformHelper.h" #include "WinDebugger.h" #include "MiniDumpDebugger.h" #include "Linker/BlHash.h" #include "Backend/BeLibManger.h" #include #include "BeefySysLib/util/AllocDebug.h" USING_NS_BF_DBG; #define MSF_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0" static const int NUM_ROOT_DIRS = 73; #pragma warning(default:4800) #define ADDR_FLAG_ABS 0x8000000000000000L #define GET(T) *((T*)(data += sizeof(T)) - 1) #define GET_INTO(T, name) T name = GET(T) #define GET_FROM(ptr, T) *((T*)(ptr += sizeof(T)) - 1) #define PTR_ALIGN(ptr, origPtr, alignSize) ptr = ( (origPtr)+( ((ptr - origPtr) + (alignSize - 1)) & ~(alignSize - 1) ) ) static const char* DataGetString(uint8*& data) { const char* prevVal = (const char*)data; while (*data != 0) data++; data++; return prevVal; } #define CREATE_PRIMITIVE(pdbTypeCode, dwTypeCode, typeName, type) \ BP_ALLOC_T(DbgType); \ dbgType = mAlloc.Alloc(); \ dbgType->mCompileUnit = mMasterCompileUnit; \ dbgType->mTypeName = typeName; \ dbgType->mName = typeName; \ dbgType->mTypeCode = dwTypeCode; \ dbgType->mSize = sizeof(type); \ dbgType->mAlign = sizeof(type); \ mCvSystemTypes[(int)pdbTypeCode] = dbgType; \ \ BP_ALLOC_T(DbgType); \ ptrType = mAlloc.Alloc(); \ ptrType->mCompileUnit = mMasterCompileUnit; \ ptrType->mTypeCode = DbgType_Ptr; \ ptrType->mSize = sizeof(addr_target); \ ptrType->mAlign = sizeof(addr_target); \ ptrType->mTypeParam = dbgType; \ dbgType->mPtrType = ptrType; \ mCvSystemTypes[(int)pdbTypeCode | ptrMask] = ptrType; #define REGISTER_PRIMITIVE() mTypeMap.Insert(dbgType) static const char* GetLastDoubleColon(const char* name) { int templateDepth = 0; int parenDepth = 0; const char* lastDblColon = NULL; for (const char* checkPtr = name; *checkPtr != '\0'; checkPtr++) { char c = checkPtr[0]; if (c == '<') templateDepth++; else if (c == '>') templateDepth--; else if (c == '(') parenDepth++; else if (c == ')') parenDepth--; if ((templateDepth == 0) && (parenDepth == 0) && (c == ':') && (checkPtr[1] == ':')) lastDblColon = checkPtr; if ((templateDepth == 0) && (parenDepth == 0) && (c == ' ')) { // This catches cases like "Beefy::BfMethodRef::operator Beefy::BfMethodInstance*", where we want to match the :: right before "operator" break; } } return lastDblColon; } static const char* GetNamespaceEnd(const char* name) { int templateDepth = 0; const char* lastDblColon = NULL; for (const char* checkPtr = name; *checkPtr != '\0'; checkPtr++) { char c = checkPtr[0]; if ((c == '<') || (c == ' ')) return NULL; if ((c == ':') && (checkPtr[1] == ':')) lastDblColon = checkPtr; } return lastDblColon; } // This version inserts namespaces that contain specialized methods, but since we don't (at the time of this comment) support // calling specialized methods, we don't bother doing this -- it increases the scan time #if 0 static const char* GetNamespaceEnd(const char* name) { bool hadTemplate = false; int templateDepth = 0; const char* lastDblColon = NULL; for (const char* checkPtr = name; *checkPtr != '\0'; checkPtr++) { char c = checkPtr[0]; if (c == '<') { hadTemplate = true; templateDepth++; } else if (c == '>') templateDepth--; if ((templateDepth == 0) && (c == ':') && (checkPtr[1] == ':')) { if (hadTemplate) { // This is to reject cases like NameSpace::TypeName::sField - since we know that there's a typename attached and we just want 'loose' namespaces, // But this still allows NameSpace::Method, where "NameSpace" is indeed a loose namespace we need to catch return NULL; } lastDblColon = checkPtr; } if ((templateDepth == 0) && (c == ' ')) { // This catches cases like "Beefy::BfMethodRef::operator Beefy::BfMethodInstance*", where we want to match the :: right before "operator" // In these cases we do know this is an operator method name so it's not in a loose namespace return NULL; } } return lastDblColon; } #endif ////////////////////////////////////////////////////////////////////////// uint8* CvStreamReader::GetTempPtr(int offset, int size, bool mayRecurse, bool* madeCopy) { int pageStart = offset >> mPageBits; int pageEnd = (offset + size - 1) >> mPageBits; if (pageStart >= pageEnd) return mStreamPtrs.mVals[pageStart] + (offset & ((1<mTempBufIdx >= mCOFF->mTempBufs.size()) mCOFF->mTempBufs.push_back(Array()); auto& arr = mCOFF->mTempBufs[mCOFF->mTempBufIdx++]; if (arr.size() < size) arr.Resize(size); destPtr = arr.mVals; } else { if (mTempData.size() < size) mTempData.Resize(size); destPtr = mTempData.mVals; } uint8* dest = destPtr; while (size > 0) { int copyPage = offset >> mPageBits; int pageOffset = offset & ((1 << mPageBits) - 1); int copyBytes = BF_MIN(mCOFF->mCvPageSize - pageOffset, size); memcpy(destPtr, mStreamPtrs.mVals[copyPage] + pageOffset, copyBytes); destPtr += copyBytes; offset += copyBytes; size -= copyBytes; } return dest; } uint8* CvStreamReader::GetPermanentPtr(int offset, int size, bool* madeCopy) { int pageStart = offset >> mPageBits; int pageEnd = (offset + size - 1) >> mPageBits; if (pageStart == pageEnd) return mStreamPtrs.mVals[pageStart] + (offset & ((1 << mPageBits) - 1)); // Handle the relatively-rare case of spanning multiple pages BP_ALLOC("GetPermanentPtr", size); uint8* destData = mCOFF->mAlloc.AllocBytes(size, "GetPermanentPtr"); if (madeCopy != NULL) *madeCopy = true; uint8* destPtr = destData; while (size > 0) { int copyPage = offset >> mPageBits; int pageOffset = offset & ((1 << mPageBits) - 1); int copyBytes = BF_MIN(mCOFF->mCvPageSize - pageOffset, size); memcpy(destPtr, mStreamPtrs.mVals[copyPage] + pageOffset, copyBytes); destPtr += copyBytes; offset += copyBytes; size -= copyBytes; } return destData; } ////////////////////////////////////////////////////////////////////////// COFF::COFF(DebugTarget* debugTarget) : DbgModule(debugTarget) { mParseKind = ParseKind_Full; memset(mWantPDBGuid, 0, 16); memset(mPDBGuid, 0, 16); mWantAge = -1; mDebugAge = -1; mFileAge = -1; mCvMinTag = -1; mCvMaxTag = -1; mCvIPIMinTag = -1; mCvIPIMaxTag = -1; mMasterCompileUnit = NULL; mTempBufIdx = 0; mCvPageSize = 0; mCvPageBits = 31; mCvDataStream = NULL; mCvHeaderData = NULL; mCvStrTableData = NULL; mCvPublicSymbolData = NULL; mCvGlobalSymbolData = NULL; mNewFPOData = NULL; mCvGlobalSymbolInfoStream = 0; mCvPublicSymbolInfoStream = 0; mCvSymbolRecordStream = 0; mCvNewFPOStream = 0; mCvMappedFile = INVALID_HANDLE_VALUE; mCvMappedFileMapping = INVALID_HANDLE_VALUE; mCvMappedViewOfFile = NULL; mCvMappedFileSize = 0; //mParsedProcRecs = false; mGlobalsTargetType = NULL; mPrevScanName = NULL; mProcSymCount = 0; mCvSrcSrvStream = -1; mIsFastLink = false; mHotThunkCurAddr = 0; mHotThunkDataLeft = 0; mTriedSymSrv = false; mDbgSymRequest = NULL; mWantsAutoLoadDebugInfo = false; mPDBLoaded = false; } COFF::~COFF() { BF_ASSERT(mTempBufIdx == 0); ClosePDB(); mDebugger->mDbgSymSrv.ReleaseRequest(mDbgSymRequest); } const char* COFF::CvCheckTargetMatch(const char* name, bool& wasBeef) { /*if (mGlobalsTargetType->IsGlobalsContainer()) { wasBeef = true; } else*/ if (mGlobalsTargetType->mLanguage == DbgLanguage_Beef) { if (strncmp(name, "_bf::", 5) != 0) return NULL; name += 5; wasBeef = true; } else if (mGlobalsTargetType->mTypeCode == DbgType_Root) { if (strncmp(name, "_bf::", 5) == 0) { wasBeef = true; name += 5; } } if (mGlobalsTargetType->mName == NULL) { for (const char* cPtr = name; *cPtr != '\0'; cPtr++) { if (*cPtr == ':') return NULL; if (*cPtr == '<') return NULL; } return name; } const char* memberNamePtr = name; const char* typeName = mGlobalsTargetType->mName; while (true) { char cm = *(memberNamePtr++); char ct = *(typeName++); if (ct == 0) { if (cm == ':') { const char* memberName = memberNamePtr + 1; for (const char* cPtr = memberName; *cPtr != '\0'; cPtr++) { if (*cPtr == ':') return NULL; if (*cPtr == '<') return NULL; } return memberName; } return NULL; } if (cm != ct) { if ((cm == ':') && (ct == '.') && (memberNamePtr[0] == ':')) { memberNamePtr++; continue; } return NULL; } } } int COFF::CvGetStringHash(const char* str) { if (str == NULL) return 0; int curHash = 0; const char* curHashPtr = str; while (*curHashPtr != 0) { char c = *curHashPtr; curHash = ((curHash ^ *curHashPtr) << 5) - curHash; curHashPtr++; } return curHash & 0x3FFFFFFF; } // Remove "__ptr64" void COFF::CvFixupName(char* name) { char* cPtrOut = NULL; for (char* cPtr = name; *cPtr != '\0'; cPtr++) { if ((cPtr[0] == '_') && (cPtr[1] == '_')) { if (strncmp(cPtr + 2, "ptr64", 5) == 0) { if (cPtrOut == NULL) cPtrOut = cPtr; cPtr += 6; } } else if (cPtrOut != NULL) { *(cPtrOut++) = *cPtr; } } if (cPtrOut != NULL) *(cPtrOut++) = '\0'; } void COFF::InitCvTypes() { if (mMasterCompileUnit != NULL) { BF_ASSERT(mCompileUnits.size() == 1); return; } mDbgFlavor = DbgFlavor_MS; mCvSystemTypes.Resize(0x700); DbgType* dbgType; DbgType* ptrType; #ifdef BF_DBG_32 const int ptrMask = 0x0400; // T_32* #else const int ptrMask = 0x0600; // T_64* #endif mMasterCompileUnit = new DbgCompileUnit(this); mMasterCompileUnit->mDbgModule = this; mMasterCompileUnit->mIsMaster = true; mCompileUnits.push_back(mMasterCompileUnit); CREATE_PRIMITIVE(T_NOTTRANS, DbgType_Void, "void", void*); mCvSystemTypes[T_NOTTRANS]->mSize = 0; mCvSystemTypes[T_NOTTRANS]->mAlign = 0; CREATE_PRIMITIVE(T_NOTYPE, DbgType_Void, "void", void*); mCvSystemTypes[T_NOTYPE]->mSize = 0; mCvSystemTypes[T_NOTYPE]->mAlign = 0; CREATE_PRIMITIVE(T_VOID, DbgType_Void, "void", void*); mCvSystemTypes[T_VOID]->mSize = 0; mCvSystemTypes[T_VOID]->mAlign = 0; mCvSystemTypes[T_PVOID] = ptrType; #ifdef BF_DBG_32 CREATE_PRIMITIVE(T_HRESULT, DbgType_u32, "HRESULT", addr_target); #else CREATE_PRIMITIVE(T_HRESULT, DbgType_u64, "HRESULT", addr_target); #endif CREATE_PRIMITIVE(T_CHAR, DbgType_SChar, "char", char); CREATE_PRIMITIVE(T_RCHAR, DbgType_SChar, "char", char); REGISTER_PRIMITIVE(); CREATE_PRIMITIVE(T_UCHAR, DbgType_UChar, "char8", uint8); REGISTER_PRIMITIVE(); CREATE_PRIMITIVE(T_WCHAR, DbgType_SChar16, "wchar", uint16); REGISTER_PRIMITIVE(); CREATE_PRIMITIVE(T_CHAR16, DbgType_UChar16, "char16", uint16); CREATE_PRIMITIVE(T_CHAR32, DbgType_UChar32, "char32", uint32); CREATE_PRIMITIVE(T_INT1, DbgType_i8, "sbyte", int8); CREATE_PRIMITIVE(T_UINT1, DbgType_u8, "byte", uint8); CREATE_PRIMITIVE(T_SHORT, DbgType_i16, "short", int16); CREATE_PRIMITIVE(T_INT2, DbgType_i16, "short", int16); CREATE_PRIMITIVE(T_USHORT, DbgType_u16, "ushort", uint16); CREATE_PRIMITIVE(T_UINT2, DbgType_u16, "ushort", uint16); CREATE_PRIMITIVE(T_LONG, DbgType_i32, "int", int32); CREATE_PRIMITIVE(T_INT4, DbgType_i32, "int", int32); CREATE_PRIMITIVE(T_ULONG, DbgType_i32, "uint", uint32); CREATE_PRIMITIVE(T_UINT4, DbgType_u32, "uint", uint32); CREATE_PRIMITIVE(T_QUAD, DbgType_i64, "int64", int64); CREATE_PRIMITIVE(T_INT8, DbgType_i64, "int64", int64); REGISTER_PRIMITIVE(); CREATE_PRIMITIVE(T_UQUAD, DbgType_u64, "uint64", uint64); CREATE_PRIMITIVE(T_UINT8, DbgType_u64, "uint64", uint64); REGISTER_PRIMITIVE(); CREATE_PRIMITIVE(T_OCT, DbgType_i128, "int128", uint128); CREATE_PRIMITIVE(T_INT16, DbgType_i128, "int128", uint128); CREATE_PRIMITIVE(T_UOCT, DbgType_u128, "uint128", uint128); CREATE_PRIMITIVE(T_UINT16, DbgType_u128, "uint128", uint128); CREATE_PRIMITIVE(T_BOOL08, DbgType_Bool, "bool", bool); CREATE_PRIMITIVE(T_BOOL32, DbgType_Bool, "bool", int32); CREATE_PRIMITIVE(T_REAL32, DbgType_Single, "float", float); CREATE_PRIMITIVE(T_REAL64, DbgType_Double, "double", double); CREATE_PRIMITIVE(T_REAL80, DbgType_Double, "double80", double); // This isn't correct } addr_target COFF::GetSectionAddr(uint16 section, uint32 offset) { if (section == 0) return offset; if ((section & 0x8000) != 0) { auto linkedModule = GetLinkedModule(); addr_target hiBase = linkedModule->mSecRelEncodingVec[section & 0x7FFF]; return hiBase + offset; } int rva = mSectionRVAs[section - 1]; if (rva == 0) return ADDR_FLAG_ABS + offset; return (addr_target)mImageBase + rva + offset; } DbgType* COFF::CvGetType(int typeId) { //TODO: How do we handle types that have the high bit set? if (typeId < 0) return NULL; /*if (typeId == 0) return NULL;*/ if (typeId < 0x1000) { TYPE_ENUM_e typeEnum = (TYPE_ENUM_e)typeId; DbgType* type = mCvSystemTypes[typeId]; BF_ASSERT(type != NULL); return type; } DbgType* type = mCvTypeMap[typeId - mCvMinTag]; if (type == NULL) type = CvParseType(typeId); /*if ((!allowNull) || (type != NULL)) { BF_ASSERT(type->mCompileUnit->mDbgModule == this); }*/ return type; } DbgType* COFF::CvGetType(int typeId, CvCompileUnit* compileUnit) { if ((typeId & 0x80000000) != 0) { return CvGetType(MapImport(compileUnit, typeId)); } return CvGetType(typeId); } int COFF::CvGetTagStart(int tagIdx, bool ipi) { if (tagIdx == 0) return NULL; if (ipi) return mCvIPITagStartMap[tagIdx - mCvMinTag]; else return mCvTagStartMap[tagIdx - mCvMinTag]; } uint8* COFF::CvGetTagData(int tagIdx, bool ipi, int* outDataSize) { if (tagIdx == 0) return NULL; auto& reader = ipi ? mCvIPIReader : mCvTypeSectionReader; int offset = ipi ? mCvIPITagStartMap[tagIdx - mCvIPIMinTag] : mCvTagStartMap[tagIdx - mCvMinTag]; uint8* data = reader.GetTempPtr(offset, 4); uint16 trLength = *(uint16*)data; data = reader.GetTempPtr(offset + 2, trLength, true); if (outDataSize != NULL) *outDataSize = trLength; return data; } int64 COFF::CvParseConstant(uint16 constVal, uint8*& data) { if (constVal < LF_NUMERIC) // 0x8000 return constVal; switch (constVal) { case LF_CHAR: return GET(int8); case LF_SHORT: return GET(int16); case LF_USHORT: return GET(uint16); case LF_LONG: return GET(int32); case LF_ULONG: return GET(uint32); case LF_QUADWORD: return GET(int64); case LF_UQUADWORD: return (int64)GET(uint64); default: BF_FATAL("Not handled"); } return 0; } int64 COFF::CvParseConstant(uint8*& data) { uint16 val = GET(uint16); return CvParseConstant(val, data); } const char* COFF::CvParseString(uint8*& data) { const char* strStart = (const char*)data; int strLen = strlen((const char*)data); if (strLen == 0) return NULL; data += strLen + 1; return strStart; } const char* COFF::CvParseAndDupString(uint8*& data) { int strLen = strlen((const char*)data); if (strLen == 0) { data++; return NULL; } BP_ALLOC("CvParseAndDupString", strLen + 1); char* dupStr = (char*)mAlloc.AllocBytes(strLen + 1, "CvParseAndDupString"); memcpy(dupStr, data, strLen); data += strLen + 1; return dupStr; } void COFF::CvParseArgList(DbgSubprogram* subprogram, int tagIdx, bool ipi) { uint8* data = CvGetTagData(tagIdx, ipi); CvAutoReleaseTempData releaseTempData(this, data); int16 trLeafType = GET(int16); BF_ASSERT(trLeafType == LF_ARGLIST); int argCount = GET(int32); for (int argIdx = 0; argIdx < argCount; argIdx++) { CV_typ_t argTypeId = GET(CV_typ_t); DbgType* argType = CvGetType(argTypeId); BP_ALLOC_T(DbgVariable); DbgVariable* arg = mAlloc.Alloc(); arg->mType = argType; arg->mIsParam = true; subprogram->mParams.PushBack(arg); } ReleaseTempBuf(data); } DbgSubprogram* COFF::CvParseMethod(DbgType* parentType, const char* methodName, int tagIdx, bool ipi, DbgSubprogram* subprogram) { BP_ZONE("COFF::CvParseMethod"); uint8* data = CvGetTagData(tagIdx, ipi); CvAutoReleaseTempData releaseTempData(this, data); bool fromTypeSection = methodName != NULL; if ((methodName != NULL) && (strcmp(methodName, "GetLength") == 0)) { NOP; } uint8* dataStart = data; int16 trLeafType = GET(int16); if (trLeafType == LF_FUNC_ID) { lfFuncId* funcData = (lfFuncId*)dataStart; subprogram = CvParseMethod(NULL, (const char*)funcData->name, funcData->type, false, subprogram); return subprogram; } if (trLeafType == LF_MFUNC_ID) { lfMFuncId* funcData = (lfMFuncId*)dataStart; auto parentType = CvGetType(funcData->parentType); //subprogram = CvParseMethod(parentType, (const char*)funcData->name, funcData->type, false, subprogram); if ((funcData->name != NULL) && (strcmp((const char*)funcData->name, "CalcNewSize") == 0)) { NOP; } // We shouldn't pass parentType in there, because that would be the declType and not the actual primary type (ie: definition type) subprogram = CvParseMethod(NULL, (const char*)funcData->name, funcData->type, false, subprogram); return subprogram; } //DbgSubprogram* subprogram = mAlloc.Alloc(); if (subprogram == NULL) { BP_ALLOC_T(DbgSubprogram); subprogram = mAlloc.Alloc(); } subprogram->mName = methodName; int argListId = 0; if (trLeafType == LF_MFUNCTION) { static int gMFuncIdx = 0; gMFuncIdx++; lfMFunc* funcData = (lfMFunc*)dataStart; argListId = funcData->arglist; subprogram->mReturnType = CvGetType(funcData->rvtype); //TODO: if (parentType == NULL) subprogram->mParentType = CvGetType(funcData->classtype); DbgType* thisType = CvGetType(funcData->thistype); if ((thisType != NULL) && (!thisType->IsVoid())) { BP_ALLOC_T(DbgVariable); DbgVariable* arg = mAlloc.Alloc(); arg->mType = thisType; arg->mIsParam = true; arg->mTagIdx = gMFuncIdx; subprogram->mParams.PushBack(arg); //TODO: Why did we have this "fromTypeSection" check? It caused non-static methods from S_GPROC32_ID to look like static methods in the autocomplete list //if (fromTypeSection) { arg->mType = thisType; subprogram->mHasThis = true; arg->mName = "this"; } } } else if (trLeafType == LF_METHODLIST) { int32 flags = GET(int32); int methodIndex = GET(int32); return CvParseMethod(parentType, methodName, methodIndex, ipi, subprogram); } else if (trLeafType == LF_PROCEDURE) { lfProc* funcData = (lfProc*)dataStart; subprogram->mReturnType = CvGetType(funcData->rvtype); argListId = funcData->arglist; } else if (trLeafType == LF_MFUNC_ID) { // } else { BF_FATAL("Unhandled func type"); } if ((parentType != NULL) && (!mIsHotObjectFile)) { subprogram->mCompileUnit = parentType->mCompileUnit; parentType->mMethodList.PushBack(subprogram); } mSubprograms.push_back(subprogram); CvParseArgList(subprogram, argListId, ipi); return subprogram; } void COFF::CvParseMethodList(DbgType* parentType, const char* methodName, int tagIdx, bool ipi) { int dataSize = 0; uint8* data = CvGetTagData(tagIdx, ipi, &dataSize); CvAutoReleaseTempData releaseTempData(this, data); uint8* dataEnd = data + dataSize; int16 trLeafType = GET(int16); BF_ASSERT(trLeafType == LF_METHODLIST); while (data < dataEnd) { int32 flags = GET(int32); int32 methodIndex = GET(int32); CvParseMethod(parentType, methodName, methodIndex, ipi); } } void COFF::CvParseMembers(DbgType* parentType, int tagIdx, bool ipi) { if (tagIdx == NULL) return; auto& reader = ipi ? mCvIPIReader : mCvTypeSectionReader; int offset = ipi ? mCvIPITagStartMap[tagIdx - mCvIPIMinTag] : mCvTagStartMap[tagIdx - mCvMinTag]; uint8* data = reader.GetTempPtr(offset, 4); uint16 trLength = *(uint16*)data; offset += 2; data = reader.GetTempPtr(offset, trLength, true); CvAutoReleaseTempData releaseTempData(this, data); uint8* dataStart = data; uint8* dataEnd = data + trLength; int16 trLeafType = GET(int16); bool strMadeCopy; auto _ParseString = [&]() { strMadeCopy = false; int nameLen = strlen((const char*)data); int nameOfs = data - dataStart; const char* name = (const char*)reader.GetPermanentPtr(offset + nameOfs, nameLen + 1, &strMadeCopy); data += nameLen + 1; return name; }; if (trLeafType == 0) return; switch (trLeafType) { case LF_FIELDLIST: { uint8* sectionStart = data; while (data < dataEnd) { uint8* leafDataStart = data; int leafType = (int)GET(uint16); switch (leafType) { case LF_VFUNCTAB: { lfVFuncTab& vfuncTab = *(lfVFuncTab*)leafDataStart; DbgType* vtableType = CvGetType(vfuncTab.type); BP_ALLOC_T(DbgVariable); DbgVariable* vtableMember = mAlloc.Alloc(); vtableMember->mType = vtableType; parentType->mMemberList.PushFront(vtableMember); data = (uint8*)(&vfuncTab + 1); } break; case LF_BCLASS: { lfBClass& baseClassInfo = *(lfBClass*)leafDataStart; BP_ALLOC_T(DbgBaseTypeEntry); DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc(); data = (uint8*)&baseClassInfo.offset; baseTypeEntry->mThisOffset = (int)CvParseConstant(data); if (baseClassInfo.index != 0) { baseTypeEntry->mBaseType = CvGetType(baseClassInfo.index); parentType->mBaseTypes.PushBack(baseTypeEntry); parentType->mAlign = std::max(parentType->mAlign, baseTypeEntry->mBaseType->GetAlign()); if (!parentType->mSizeCalculated) { if ((parentType->mSize == 0) && (baseTypeEntry->mBaseType->IsBfObject())) { parentType->mExtType = DbgExtType_Interface; } parentType->mSize = BF_MAX(parentType->mSize, baseTypeEntry->mBaseType->GetByteCount()); } } } break; case LF_VBCLASS: case LF_IVBCLASS: { lfVBClass& baseClassInfo = *(lfVBClass*)leafDataStart; BP_ALLOC_T(DbgBaseTypeEntry); DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc(); baseTypeEntry->mBaseType = CvGetType(baseClassInfo.index); data = (uint8*)&baseClassInfo.vbpoff; baseTypeEntry->mThisOffset = (int)CvParseConstant(data); baseTypeEntry->mVTableOffset = (int)CvParseConstant(data); parentType->mBaseTypes.PushBack(baseTypeEntry); } break; case LF_ENUMERATE: { CV_fldattr_t fieldAttr = GET(CV_fldattr_t); int64 fieldVal = CvParseConstant(data); const char* fieldName = _ParseString(); BP_ALLOC_T(DbgVariable); DbgVariable* member = mAlloc.Alloc(); member->mCompileUnit = parentType->mCompileUnit; member->mConstValue = fieldVal; member->mName = fieldName; member->mIsStatic = true; member->mIsConst = true; member->mType = parentType->mTypeParam; FixConstant(member); parentType->mMemberList.PushBack(member); } break; case LF_NESTTYPE: { // Resolve nested types at end through 'primary type' // Nested types also include nested TypeDefs, so the embedded type index may even be a primitive type here // Why did we have this check to avoid unnamed nested types? // it seems we added this and removed it a couple times. // Disallowing NULL nested types here breaks the "_BUF_SIZE" in String, which is // an anonymous enum /*int16 pad = GET(int16); int32 nestedTypeId = GET(int32); const char* typeName = CvParseAndDupString(data); if ((typeName != NULL) && (typeName[0] != '<')) { DbgType* nestedType = CvCreateType(); nestedType->mTypeParam = CvGetType(nestedTypeId); nestedType->mTypeCode = DbgType_TypeDef; nestedType->mName = typeName; nestedType->mTypeName = typeName; nestedType->mParent = parentType; parentType->mSubTypeList.PushBack(nestedType); }*/ int16 pad = GET(int16); int32 nestedTypeId = GET(int32); const char* typeName = _ParseString(); DbgType* nestedType = CvCreateType(); nestedType->mTypeParam = CvGetType(nestedTypeId); nestedType->mTypeCode = DbgType_TypeDef; if ((typeName != NULL) && (typeName[0] != '<')) { nestedType->mName = typeName; nestedType->mTypeName = typeName; } nestedType->mParent = parentType; parentType->mSubTypeList.PushBack(nestedType); } break; case LF_ONEMETHOD: { CV_fldattr_t attr = GET(CV_fldattr_t); CV_typ_t methodTypeId = GET(CV_typ_t); int virtOffset = -1; if ((attr.mprop == CV_MTintro) || (attr.mprop == CV_MTpureintro)) virtOffset = GET(int32); const char* methodName = _ParseString(); DbgSubprogram* subProgram = CvParseMethod(parentType, methodName, methodTypeId, ipi); subProgram->mVirtual = (attr.mprop == CV_MTintro) || (attr.mprop == CV_MTpureintro) || (attr.mprop == CV_MTvirtual); subProgram->mVTableLoc = virtOffset; } break; case LF_METHOD: { int count = (int)GET(uint16); int32 methodList = GET(int32); const char* methodName = _ParseString(); uint8* listData = CvGetTagData(methodList, ipi); CvAutoReleaseTempData releaseTempData(this, listData); int16 trLeafType = GET_FROM(listData, int16); BF_ASSERT(trLeafType == LF_METHODLIST); for (int methodIdx = 0; methodIdx < count; methodIdx++) { CV_fldattr_t attr = GET_FROM(listData, CV_fldattr_t); int16 unused = GET_FROM(listData, int16); CV_typ_t methodTypeId = GET_FROM(listData, CV_typ_t); int virtOffset = -1; if ((attr.mprop == CV_MTintro) || (attr.mprop == CV_MTpureintro)) virtOffset = GET_FROM(listData, int32); DbgSubprogram* subProgram = CvParseMethod(parentType, methodName, methodTypeId, ipi); subProgram->mVirtual = (attr.mprop == CV_MTintro) || (attr.mprop == CV_MTpureintro) || (attr.mprop == CV_MTvirtual); subProgram->mVTableLoc = virtOffset; } } break; case LF_MEMBER: case LF_STMEMBER: { bool isStatic = leafType == LF_STMEMBER; bool isConst = false; CV_fldattr_t attr = GET(CV_fldattr_t); CV_typ_t fieldTypeId = GET(CV_typ_t); if (parentType->mTagIdx == 29184) { NOP; } int memberOffset = -1; if (isStatic) { //? } else { memberOffset = (int)CvParseConstant(data); } char* fieldName = (char*)_ParseString(); if ((fieldName != NULL) && (strcmp(fieldName, "$prim") == 0)) { DbgType* fieldType = CvGetType(fieldTypeId); DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc(); baseTypeEntry->mThisOffset = 0; baseTypeEntry->mBaseType = fieldType; parentType->mBaseTypes.PushFront(baseTypeEntry); break; } int64 constVal = 0; if ((parentType->mLanguage == DbgLanguage_Beef) && (isStatic)) { for (char* cPtr = fieldName; true; cPtr++) { char c = *cPtr; if (c == 0) break; if (c == '$') { if (!strMadeCopy) { int ofs = cPtr - fieldName; fieldName = DbgDupString(fieldName, "CvParseMembers.LF_MEMBER"); cPtr = fieldName + ofs; } isConst = true; if (cPtr[1] == '_') constVal = -atoll(cPtr + 2); else constVal = atoll(cPtr + 1); *cPtr = 0; break; } } } if ((isStatic) && (!isConst) && (mIsHotObjectFile)) { // Already has statics filled in break; } BP_ALLOC_T(DbgVariable); DbgVariable* member = mAlloc.Alloc(); member->mIsStatic = isStatic; DbgType* fieldType = CvGetType(fieldTypeId); // if (fieldType == NULL) // { // uint8* fieldTypeData = CvGetTagData(fieldTypeId, ipi); // CvAutoReleaseTempData releaseTempData(this, fieldTypeData); // // // It's a section data ptr // int16 memberLeafType = *((int16*)fieldTypeData); // switch (memberLeafType) // { // case LF_BITFIELD: // { // lfBitfield& bitfield = *(lfBitfield*)fieldTypeData; // fieldType = CvGetType(bitfield.type); // // // Bit offset is expressed in MSB form // member->mBitOffset = (fieldType->mSize * 8) - bitfield.position - bitfield.length; // member->mBitSize = bitfield.length; // } // break; // default: // BF_FATAL("Unhandled"); // } // } if (fieldType == NULL) fieldType = CvGetType(fieldTypeId); if ((fieldType->mTypeCode == DbgType_Enum) && (fieldType->GetByteCount() == 0)) fieldType = fieldType->GetPrimaryType(); // if (fieldType->mTypeCode == DbgType_Bitfield) // { // auto bitfieldType = (DbgBitfieldType*)fieldType; // member->mBitOffset = bitfieldType->mPosition; // member->mBitSize = bitfieldType->mLength; // fieldType = fieldType->mTypeParam; // } if (isConst) { member->mConstValue = constVal; member->mIsConst = true; } else if (isStatic) { // NOP } else member->mMemberOffset = memberOffset; member->mType = fieldType; member->mCompileUnit = parentType->mCompileUnit; member->mName = fieldName; // Size should already be set, right? It gets set on 'dataSize' in the LF_STRUCT/LF_CLASS //parentType->mSize = std::max(memberOffset + fieldType->mSize, parentType->mSize); if ((!isStatic) && (member->mMemberOffset >= 0)) { if (!parentType->mIsPacked) parentType->mAlign = std::max(parentType->mAlign, fieldType->GetAlign()); if (!parentType->mSizeCalculated) { parentType->mSize = std::max(memberOffset + fieldType->GetByteCount(), parentType->mSize); } } //if (parentType->mAlign > 1) //parentType->mSize = (parentType->mSize + (parentType->mAlign - 1)) & ~(parentType->mAlign - 1); //parentType->mSizeCalculated = true; parentType->mMemberList.PushBack(member); if (isStatic) parentType->mNeedsGlobalsPopulated = true; } break; case LF_INDEX: { int _pad = (int)GET(uint16); int32 indexType = GET(int32); CvParseMembers(parentType, indexType, ipi); } break; default: BF_FATAL("Unhandled"); } PTR_ALIGN(data, sectionStart, 4); } } break; } } int COFF::CvConvRegNum(int regNum, int* outBits) { if ((regNum >= CV_AMD64_R8B) && (regNum <= CV_AMD64_R15B)) { if (outBits != NULL) *outBits = 8; regNum -= CV_AMD64_R8B - CV_AMD64_R8; } else if ((regNum >= CV_AMD64_R8W) && (regNum <= CV_AMD64_R15W)) { if (outBits != NULL) *outBits = 16; regNum -= CV_AMD64_R8W - CV_AMD64_R8; } else if ((regNum >= CV_AMD64_R8D) && (regNum <= CV_AMD64_R15D)) { if (outBits != NULL) *outBits = 32; regNum -= CV_AMD64_R8D - CV_AMD64_R8; } switch (regNum) { #ifdef BF_DBG_64 case CV_AMD64_RAX: return X64Reg_RAX; case CV_AMD64_RDX: return X64Reg_RDX; case CV_AMD64_RCX: return X64Reg_RCX; case CV_AMD64_RBX: return X64Reg_RBX; case CV_AMD64_RSI: return X64Reg_RSI; case CV_AMD64_RDI: return X64Reg_RDI; case CV_AMD64_RBP: return X64Reg_RBP; case CV_AMD64_RSP: return X64Reg_RSP; case CV_AMD64_R8: return X64Reg_R8; case CV_AMD64_R9: return X64Reg_R9; case CV_AMD64_R10: return X64Reg_R10; case CV_AMD64_R11: return X64Reg_R11; case CV_AMD64_R12: return X64Reg_R12; case CV_AMD64_R13: return X64Reg_R13; case CV_AMD64_R14: return X64Reg_R14; case CV_AMD64_R15: return X64Reg_R15; case CV_AMD64_RIP: return X64Reg_RIP; case CV_REG_AL: if (outBits != NULL) *outBits = 8; return X64Reg_RAX; case CV_REG_AX: if (outBits != NULL) *outBits = 16; return X64Reg_RAX; case CV_REG_EAX: if (outBits != NULL) *outBits = 32; return X64Reg_RAX; case CV_REG_CL: if (outBits != NULL) *outBits = 8; return X64Reg_RCX; case CV_REG_CX: if (outBits != NULL) *outBits = 16; return X64Reg_RCX; case CV_REG_ECX: if (outBits != NULL) *outBits = 32; return X64Reg_RCX; case CV_REG_DL: if (outBits != NULL) *outBits = 8; return X64Reg_RDX; case CV_REG_DX: if (outBits != NULL) *outBits = 16; return X64Reg_RDX; case CV_REG_EDX: if (outBits != NULL) *outBits = 32; return X64Reg_RDX; case CV_REG_BL: if (outBits != NULL) *outBits = 8; return X64Reg_RBX; case CV_REG_BX: if (outBits != NULL) *outBits = 16; return X64Reg_RBX; case CV_REG_EBX: if (outBits != NULL) *outBits = 32; return X64Reg_RBX; case CV_REG_ESP: if (outBits != NULL) *outBits = 32; return X64Reg_RSP; case CV_REG_EBP: if (outBits != NULL) *outBits = 32; return X64Reg_RBP; case CV_AMD64_SIL: if (outBits != NULL) *outBits = 8; return X64Reg_RSI; case CV_REG_SI: if (outBits != NULL) *outBits = 16; return X64Reg_RSI; case CV_REG_ESI: if (outBits != NULL) *outBits = 32; return X64Reg_RSI; case CV_AMD64_DIL: if (outBits != NULL) *outBits = 8; return X64Reg_RDI; case CV_REG_DI: if (outBits != NULL) *outBits = 16; return X64Reg_RDI; case CV_REG_EDI: if (outBits != NULL) *outBits = 32; return X64Reg_RDI; case CV_AMD64_R8B: *outBits = 8; return X64Reg_R8; case CV_AMD64_R8W: *outBits = 16; return X64Reg_R8; case CV_AMD64_R8D: *outBits = 32; return X64Reg_R8; case CV_AMD64_R9B: *outBits = 8; return X64Reg_R9; case CV_AMD64_R9W: *outBits = 16; return X64Reg_R9; case CV_AMD64_R9D: *outBits = 32; return X64Reg_R9; case CV_AMD64_R10B: *outBits = 8; return X64Reg_R10; case CV_AMD64_R10W: *outBits = 16; return X64Reg_R10; case CV_AMD64_R10D: *outBits = 32; return X64Reg_R10; case CV_AMD64_R11B: *outBits = 8; return X64Reg_R11; case CV_AMD64_R11W: *outBits = 16; return X64Reg_R11; case CV_AMD64_R11D: *outBits = 32; return X64Reg_R11; case CV_AMD64_R12B: *outBits = 8; return X64Reg_R12; case CV_AMD64_R12W: *outBits = 16; return X64Reg_R12; case CV_AMD64_R12D: *outBits = 32; return X64Reg_R12; case CV_AMD64_R13B: *outBits = 8; return X64Reg_R13; case CV_AMD64_R13W: *outBits = 16; return X64Reg_R13; case CV_AMD64_R13D: *outBits = 32; return X64Reg_R13; case CV_AMD64_R14B: *outBits = 8; return X64Reg_R14; case CV_AMD64_R14W: *outBits = 16; return X64Reg_R14; case CV_AMD64_R14D: *outBits = 32; return X64Reg_R14; case CV_AMD64_R15B: *outBits = 8; return X64Reg_R15; case CV_AMD64_R15W: *outBits = 16; return X64Reg_R15; case CV_AMD64_R15D: *outBits = 32; return X64Reg_R15; case CV_AMD64_XMM0_0: return X64Reg_XMM00; case CV_AMD64_XMM0_1: return X64Reg_XMM01; case CV_AMD64_XMM0_2: return X64Reg_XMM02; case CV_AMD64_XMM0_3: return X64Reg_XMM03; case CV_AMD64_XMM1_0: return X64Reg_XMM10; case CV_AMD64_XMM1_1: return X64Reg_XMM11; case CV_AMD64_XMM1_2: return X64Reg_XMM12; case CV_AMD64_XMM1_3: return X64Reg_XMM13; case CV_AMD64_XMM2_0: return X64Reg_XMM20; case CV_AMD64_XMM2_1: return X64Reg_XMM21; case CV_AMD64_XMM2_2: return X64Reg_XMM22; case CV_AMD64_XMM2_3: return X64Reg_XMM23; case CV_AMD64_XMM3_0: return X64Reg_XMM30; case CV_AMD64_XMM3_1: return X64Reg_XMM31; case CV_AMD64_XMM3_2: return X64Reg_XMM32; case CV_AMD64_XMM3_3: return X64Reg_XMM33; case CV_AMD64_XMM4_0: return X64Reg_XMM40; case CV_AMD64_XMM4_1: return X64Reg_XMM41; case CV_AMD64_XMM4_2: return X64Reg_XMM42; case CV_AMD64_XMM4_3: return X64Reg_XMM43; case CV_AMD64_XMM5_0: return X64Reg_XMM50; case CV_AMD64_XMM5_1: return X64Reg_XMM51; case CV_AMD64_XMM5_2: return X64Reg_XMM52; case CV_AMD64_XMM5_3: return X64Reg_XMM53; case CV_AMD64_XMM6_0: return X64Reg_XMM60; case CV_AMD64_XMM6_1: return X64Reg_XMM61; case CV_AMD64_XMM6_2: return X64Reg_XMM62; case CV_AMD64_XMM6_3: return X64Reg_XMM63; case CV_AMD64_XMM7_0: return X64Reg_XMM70; case CV_AMD64_XMM7_1: return X64Reg_XMM71; case CV_AMD64_XMM7_2: return X64Reg_XMM72; case CV_AMD64_XMM7_3: return X64Reg_XMM73; case CV_AMD64_XMM8_0: return X64Reg_XMM80; case CV_AMD64_XMM8_1: return X64Reg_XMM81; case CV_AMD64_XMM8_2: return X64Reg_XMM82; case CV_AMD64_XMM8_3: return X64Reg_XMM83; case CV_AMD64_XMM9_0: return X64Reg_XMM90; case CV_AMD64_XMM9_1: return X64Reg_XMM91; case CV_AMD64_XMM9_2: return X64Reg_XMM92; case CV_AMD64_XMM9_3: return X64Reg_XMM93; case CV_AMD64_XMM10_0: return X64Reg_XMM10_0; case CV_AMD64_XMM10_1: return X64Reg_XMM10_1; case CV_AMD64_XMM10_2: return X64Reg_XMM10_2; case CV_AMD64_XMM10_3: return X64Reg_XMM10_3; case CV_AMD64_XMM11_0: return X64Reg_XMM11_0; case CV_AMD64_XMM11_1: return X64Reg_XMM11_1; case CV_AMD64_XMM11_2: return X64Reg_XMM11_2; case CV_AMD64_XMM11_3: return X64Reg_XMM11_3; case CV_AMD64_XMM12_0: return X64Reg_XMM12_0; case CV_AMD64_XMM12_1: return X64Reg_XMM12_1; case CV_AMD64_XMM12_2: return X64Reg_XMM12_2; case CV_AMD64_XMM12_3: return X64Reg_XMM12_3; case CV_AMD64_XMM13_0: return X64Reg_XMM13_0; case CV_AMD64_XMM13_1: return X64Reg_XMM13_1; case CV_AMD64_XMM13_2: return X64Reg_XMM13_2; case CV_AMD64_XMM13_3: return X64Reg_XMM13_3; case CV_AMD64_XMM14_0: return X64Reg_XMM14_0; case CV_AMD64_XMM14_1: return X64Reg_XMM14_1; case CV_AMD64_XMM14_2: return X64Reg_XMM14_2; case CV_AMD64_XMM14_3: return X64Reg_XMM14_3; case CV_AMD64_XMM15_0: return X64Reg_XMM15_0; case CV_AMD64_XMM15_1: return X64Reg_XMM15_1; case CV_AMD64_XMM15_2: return X64Reg_XMM15_2; case CV_AMD64_XMM15_3: return X64Reg_XMM15_3; #else case CV_REG_AL: return X86Reg_EAX; case CV_REG_CL: return X86Reg_ECX; case CV_REG_DL: return X86Reg_EDX; case CV_REG_BL: return X86Reg_EBX; case CV_REG_AH: return X86Reg_EAX; case CV_REG_AX: return X86Reg_EAX; case CV_REG_CX: return X86Reg_ECX; case CV_REG_DX: return X86Reg_EDX; case CV_REG_BX: return X86Reg_EBX; case CV_REG_SP: return X86Reg_ESP; case CV_REG_BP: return X86Reg_EBP; case CV_REG_SI: return X86Reg_ESI; case CV_REG_DI: return X86Reg_EDI; case CV_REG_EAX: return X86Reg_EAX; case CV_REG_ECX: return X86Reg_ECX; case CV_REG_EDX: return X86Reg_EDX; case CV_REG_EBX: return X86Reg_EBX; case CV_REG_ESP: return X86Reg_ESP; case CV_REG_EBP: return X86Reg_EBP; case CV_REG_ESI: return X86Reg_ESI; case CV_REG_EDI: return X86Reg_EDI; case CV_REG_IP: return X86Reg_EIP; // case CV_REG_XMM0: return X86Reg_XMM00; // case CV_REG_XMM1: return X86Reg_XMM01; // case CV_REG_XMM2: return X86Reg_XMM02; // case CV_REG_XMM3: return X86Reg_XMM03; // case CV_REG_XMM4: return X86Reg_XMM04; // case CV_REG_XMM5: return X86Reg_XMM05; // case CV_REG_XMM6: return X86Reg_XMM06; // case CV_REG_XMM7: return X86Reg_XMM07; #endif } return 0; // Nope BF_FATAL("Invalid register"); return 0; } DbgType* COFF::CvCreateType() { DbgModule* linkedModule = GetLinkedModule(); BP_ALLOC_T(DbgType); DbgType* dbgType = mAlloc.Alloc(); dbgType->mCompileUnit = mMasterCompileUnit; dbgType->mTypeIdx = (int)linkedModule->mTypes.size(); linkedModule->mTypes.push_back(dbgType); return dbgType; } DbgType* COFF::CvParseType(int tagIdx, bool ipi) { auto& reader = ipi ? mCvIPIReader : mCvTypeSectionReader; int offset = ipi ? mCvIPITagStartMap[tagIdx - mCvIPIMinTag] : mCvTagStartMap[tagIdx - mCvMinTag]; uint8* data = reader.GetTempPtr(offset, 4); uint16 trLength = *(uint16*)data; offset += 2; data = reader.GetTempPtr(offset, trLength, true); CvAutoReleaseTempData releaseTempData(this, data); uint8* dataStart = data; uint8* dataEnd = data + trLength; int16 trLeafType = GET(int16); bool strMadeCopy; auto _ParseString = [&]() { strMadeCopy = false; int nameLen = strlen((const char*)data); int nameOfs = data - dataStart; const char* name = (const char*)reader.GetPermanentPtr(offset + nameOfs, nameLen + 1, &strMadeCopy); data += nameLen + 1; return name; }; DbgType* dbgType = NULL; switch (trLeafType) { case LF_ENUM: { int elemCount = (int)GET(uint16); int prop = (int)GET(uint16); int underlyingType = GET(int32); int typeIndex = GET(int32); const char* name = _ParseString(); dbgType = CvCreateType(); dbgType->mCompileUnit = mMasterCompileUnit; dbgType->mName = name; dbgType->mTypeName = name; //SplitName(dbgType->mName, dbgType->mTypeName, dbgType->mTemplateParams); dbgType->mTypeCode = DbgType_Enum; dbgType->mTypeParam = CvGetType(underlyingType); dbgType->mIsIncomplete = true; if (dbgType->mTypeParam->GetByteCount() == 0) dbgType->mIsDeclaration = true; dbgType->mSize = dbgType->mTypeParam->mSize; dbgType->mAlign = dbgType->mTypeParam->mAlign; /*if (dbgType->mTypeParam->GetByteCount() == 0) { // Appears to be a bug where byte-sized enums come out as void (sometimes?) dbgType->mTypeParam = mBfPrimitiveTypes[DbgType_u8]; }*/ //dbgType->mIsDeclaration = typeIndex == 0; //CvParseMembers(dbgType, typeIndex, sectionData); } break; case LF_VTSHAPE: { lfVTShape& vtShape = *(lfVTShape*)dataStart; dbgType = CvCreateType(); dbgType->mTypeCode = DbgType_VTable; dbgType->mSize = vtShape.count * sizeof(addr_target); dbgType->mSizeCalculated = true; } break; case LF_BITFIELD: { //DbgType lfBitfield& bitfield = *(lfBitfield*)dataStart; //dbgType = CvCreateType(); DbgBitfieldType* bitfieldType = mAlloc.Alloc(); dbgType = bitfieldType; dbgType->mTypeParam = CvGetType(bitfield.type); dbgType->mTypeCode = DbgType_Bitfield; //bitfieldType->mPosition = (dbgType->mTypeParam->mSize * 8) - bitfield.position - bitfield.length; bitfieldType->mPosition = bitfield.position; bitfieldType->mLength = bitfield.length; bitfieldType->mSize = dbgType->mTypeParam->mSize; bitfieldType->mAlign = dbgType->mTypeParam->mAlign; dbgType->mSizeCalculated = true; dbgType->mPriority = DbgTypePriority_Unique; } break; case LF_STRUCTURE: case LF_CLASS: { lfClass& classInfo = *(lfClass*)dataStart; data = (uint8*)&classInfo.data; int dataSize = (int)CvParseConstant(data); const char* name = _ParseString(); // if ((strstr(name, "`") != NULL) || (strstr(name, "::__l") != NULL)) // { // OutputDebugStrF("Local type: %s\n", name); // } // Remove "enum " from type names for (int i = 0; true; i++) { char c = name[i]; if (c == 0) break; if ((c == ' ') && (i >= 5)) { if ((name[i - 4] == 'e') && (name[i - 3] == 'n') && (name[i - 2] == 'u') && (name[i - 1] == 'm')) { char prevC = name[i - 5]; if ((!isalnum(prevC)) && (prevC != '_')) { if (!strMadeCopy) name = DbgDupString(name, "CvParseType.LF_CLASS"); memmove((char*)name + i - 4, name + i + 1, strlen(name + i + 1) + 1); i -= 5; continue; } } } } dbgType = CvCreateType(); dbgType->mCompileUnit = mMasterCompileUnit; dbgType->mIsDeclaration = classInfo.property.fwdref; dbgType->mName = name; dbgType->mTypeName = name; //SplitName(dbgType->mName, dbgType->mTypeName, dbgType->mTemplateParams); if (trLeafType == LF_CLASS) dbgType->mTypeCode = DbgType_Class; else dbgType->mTypeCode = DbgType_Struct; DbgType* baseType = NULL; if (classInfo.derived != 0) { baseType = CvGetType(classInfo.derived); BP_ALLOC_T(DbgBaseTypeEntry); DbgBaseTypeEntry* baseTypeEntry = mAlloc.Alloc(); baseTypeEntry->mBaseType = baseType; dbgType->mBaseTypes.PushBack(baseTypeEntry); } if (classInfo.property.packed) { dbgType->mAlign = 1; dbgType->mIsPacked = true; } if (!dbgType->mIsDeclaration) { dbgType->mSizeCalculated = true; dbgType->mSize = dataSize; if (strncmp(name, "_bf:", 4) == 0) { // Beef types have different strides from size dbgType->mSizeCalculated = false; } } if (classInfo.vshape != 0) { CvGetType(classInfo.vshape); dbgType->mHasVTable = true; } dbgType->mIsIncomplete = true; //CvParseMembers(dbgType, classInfo.field, sectionData); } break; case LF_UNION: { lfUnion& classInfo = *(lfUnion*)dataStart; data = (uint8*)&classInfo.data; int dataSize = (int)CvParseConstant(data); const char* name = _ParseString(); dbgType = CvCreateType(); dbgType->mCompileUnit = mMasterCompileUnit; dbgType->mIsDeclaration = classInfo.property.fwdref; dbgType->mName = name; dbgType->mTypeName = name; //SplitName(dbgType->mName, dbgType->mTypeName, dbgType->mTemplateParams); dbgType->mTypeCode = DbgType_Union; dbgType->mSize = dataSize; dbgType->mAlign = 1; dbgType->mSizeCalculated = !dbgType->mIsDeclaration; dbgType->mIsIncomplete = true; //CvParseMembers(dbgType, classInfo.field, sectionData); } break; case LF_MODIFIER: { lfModifier& modifier = *(lfModifier*)dataStart; DbgType* outerType = CvGetType(modifier.type); dbgType = outerType; if (modifier.attr.MOD_const) { DbgType* innerType = dbgType; dbgType = CvCreateType(); dbgType->mTypeParam = innerType; dbgType->mTypeCode = DbgType_Const; dbgType->mLanguage = innerType->mLanguage; } if (modifier.attr.MOD_volatile) { DbgType* innerType = dbgType; dbgType = CvCreateType(); dbgType->mTypeParam = innerType; dbgType->mTypeCode = DbgType_Volatile; dbgType->mLanguage = innerType->mLanguage; } if (modifier.attr.MOD_unaligned) { DbgType* innerType = dbgType; dbgType = CvCreateType(); dbgType->mTypeParam = innerType; dbgType->mTypeCode = DbgType_Unaligned; dbgType->mLanguage = innerType->mLanguage; } } break; case LF_POINTER: { lfPointer* pointerInfo = (lfPointer*)dataStart; dbgType = CvCreateType(); dbgType->mTypeParam = CvGetType(pointerInfo->utype); if (pointerInfo->attr.ptrmode == CV_PTR_MODE_RVREF) dbgType->mTypeCode = DbgType_RValueReference; else if (pointerInfo->attr.ptrmode == CV_PTR_MODE_LVREF) dbgType->mTypeCode = DbgType_Ref; else { dbgType->mTypeCode = DbgType_Ptr; if ((dbgType->mTypeParam != NULL) && (dbgType->mTypeParam->mPtrType == NULL)) dbgType->mTypeParam->mPtrType = dbgType; } //dbgType->mSize = pointerInfo->attr.size; dbgType->mSize = sizeof(addr_target); dbgType->mAlign = dbgType->mSize; dbgType->mSizeCalculated = true; dbgType->mLanguage = dbgType->mTypeParam->mLanguage; /*if (prop.isconst) { DbgType* innerType = dbgType; dbgType = CvCreateType(); dbgType->mTypeParam = innerType; dbgType->mTypeCode = DbgType_Const; dbgType->mSize = innerType->mSize; }*/ //TODO: Handle const, volatile, ref, restrict, etc } break; case LF_DIMARRAY: { } break; case LF_ARRAY: { lfArray* array = (lfArray*)dataStart; DbgType* indexType = CvGetType(array->idxtype); dbgType = CvCreateType(); dbgType->mTypeParam = CvGetType(array->elemtype); dbgType->mTypeCode = DbgType_SizedArray; dbgType->mLanguage = dbgType->mTypeParam->mLanguage; data = (uint8*)&array->data; int size = (int)CvParseConstant(data); const char* name = _ParseString(); dbgType->mName = name; dbgType->mSize = size; // Beef arrays do not necessarily have aligned sizes dbgType->mSizeCalculated = false; dbgType->mLanguage = dbgType->mTypeParam->mLanguage; } break; case LF_PROCEDURE: { dbgType = CvCreateType(); //TODO: Calling convetion, etc lfProc* proc = (lfProc*)dataStart; dbgType->mTypeCode = DbgType_Subroutine; dbgType->mTypeParam = CvGetType(proc->rvtype); BP_ALLOC_T(DbgBlock); dbgType->mBlockParam = mAlloc.Alloc(); uint8* argData = CvGetTagData(proc->arglist, ipi); CvAutoReleaseTempData releaseTempData(this, argData); int16 argLeafType = GET_FROM(argData, int16); BF_ASSERT(argLeafType == LF_ARGLIST); int argCount = GET_FROM(argData, int32); CV_typ_t* argTypes = (CV_typ_t*)argData; for (int paramIdx = 0; paramIdx < proc->parmcount; paramIdx++) { BP_ALLOC_T(DbgVariable); DbgVariable* arg = mAlloc.Alloc(); arg->mIsParam = true; arg->mType = CvGetType(argTypes[paramIdx]); arg->mName = "$arg"; dbgType->mBlockParam->mVariables.PushBack(arg); } } break; case LF_MFUNCTION: { dbgType = CvCreateType(); //TODO: Calling convetion, etc lfMFunc* proc = (lfMFunc*)dataStart; dbgType->mTypeCode = DbgType_Subroutine; dbgType->mTypeParam = CvGetType(proc->rvtype); BP_ALLOC_T(DbgBlock); dbgType->mBlockParam = mAlloc.Alloc(); uint8* argData = CvGetTagData(proc->arglist, ipi); CvAutoReleaseTempData releaseTempData(this, argData); int16 argLeafType = GET_FROM(argData, int16); BF_ASSERT(argLeafType == LF_ARGLIST); int argCount = GET_FROM(argData, int32); CV_typ_t* argTypes = (CV_typ_t*)argData; for (int paramIdx = 0; paramIdx < proc->parmcount; paramIdx++) { BP_ALLOC_T(DbgVariable); DbgVariable* arg = mAlloc.Alloc(); arg->mIsParam = true; arg->mType = CvGetType(argTypes[paramIdx]); arg->mName = "$arg"; dbgType->mBlockParam->mVariables.PushBack(arg); } } break; } if (dbgType != NULL) { if (tagIdx == 14709) { NOP; } dbgType->mTagIdx = tagIdx; int wantIdx = tagIdx - mCvMinTag; mCvTypeMap[wantIdx] = dbgType; } return dbgType; } void COFF::ParseTypeData(CvStreamReader& reader, int dataOffset) { BP_ZONE("COFF::ParseTypeData"); //uint8* sectionEnd = sectionData + sectionSize; //uint8* data = sectionData; if (mCvMinTag == -1) mCvMinTag = 0x1000; int offset = dataOffset; bool isTagMapEmpty = mCvTagStartMap.empty(); for (int tagIdx = mCvMinTag; true; tagIdx++) { if (tagIdx == mCvMaxTag) break; if (offset >= reader.mSize) break; //OutputDebugStrF("%X %X\n", tagIdx, (int)(data - sectionData)); BF_ASSERT(((offset) & 3) == 0); //PTR_ALIGN(data, sectionData, 4); uint8* data = reader.GetTempPtr(offset, 4); uint16 trLength = *(uint16*)data; uint16 trLeafType = *(uint16*)(data + 2); // uint16 trLength = GET(uint16); // uint8* dataStart = data; // uint16 trLeafType = GET(uint16); // uint8* dataEnd = dataStart + trLength; if (isTagMapEmpty) { mCvTagStartMap.push_back(offset); mCvTypeMap.push_back(NULL); } else mCvTagStartMap[tagIdx - mCvMinTag] = offset; offset += trLength + 2; DbgType* dbgType = NULL; if (trLeafType == 0) continue; if ((trLeafType == LF_ENUM) || (trLeafType == LF_CLASS) || (trLeafType == LF_STRUCTURE) || (trLeafType == LF_UNION)) { CvParseType(tagIdx); } } } void COFF::ParseTypeData(int sectionNum, CvStreamReader& reader, int& sectionSize, int& dataOfs, int& hashStream, int& hashAdjOffset, int& hashAdjSize, int& minVal, int& maxVal) { BP_ZONE("COFF::ParseTypeData"); sectionSize = 0; // sectionData = CvReadStream(sectionNum, §ionSize); // if (sectionData == NULL) // return; // uint8* data = sectionData; CvReadStream(sectionNum, reader); uint8* data = reader.GetTempPtr(0, 0); uint8* sectionData = data; int32 ver = GET(int32); int32 headerSize = GET(int32); minVal = GET(int32); maxVal = GET(int32); int32 followSize = GET(int32); hashStream = GET(int16); int16 hashStreamPadding = GET(int16); // -1 int32 hashKeySize = GET(int32); // 4 int32 hashBucketsSize = GET(int32); // 0x3ffff int32 hashValsOffset = GET(int32); // 0 int32 hashValsSize = GET(int32); int32 hashTypeIndexOffset = GET(int32); int32 hashTypeIndexSize = GET(int32); hashAdjOffset = GET(int32); hashAdjSize = GET(int32); dataOfs = 14 * 4; uint8* typeDataHead = data; bool validate = false; // Validate hash info - not necessary, just for debugging if (validate) { BF_FATAL("Fix validate stuff to work with new CvReader"); int hashStreamSize = 0; uint8* hashDataHead = CvReadStream(hashStream, &hashStreamSize); int tagId = minVal; //////// Validate Type indices int typeIndexCount = hashTypeIndexSize / 8; // We expect a TypeIndexOffset for every 8k of data, plus the zero at the start int expectedTypeIndices = (sectionSize - (int)(data - sectionData)) / 8192 + 1; // Assert it's in some acceptable range... BF_ASSERT((typeIndexCount > expectedTypeIndices / 2) && (typeIndexCount < expectedTypeIndices * 2)); for (int entryIdx = 0; entryIdx < typeIndexCount; entryIdx++) { data = hashDataHead + hashTypeIndexOffset + entryIdx * 8; GET_INTO(int, typeIdx); GET_INTO(int, ofs); uint8* dataEnd; int endTagId; if (entryIdx < typeIndexCount - 1) { endTagId = GET(int); int endOfs = GET(int); dataEnd = typeDataHead + endOfs; } else { endTagId = maxVal; dataEnd = sectionData + sectionSize; } data = typeDataHead + ofs; while (data < dataEnd) { int tagSize = GET(uint16); data += tagSize; tagId++; } BF_ASSERT(data == dataEnd); BF_ASSERT(tagId == endTagId); } BF_ASSERT(tagId == maxVal); ////// Validate hashes int hashCount = hashValsSize / 4; BF_ASSERT(hashCount == maxVal - minVal); tagId = minVal; int32* hashVals = (int32*)(hashDataHead + hashValsOffset); data = typeDataHead; Array tagStarts; tagStarts.Reserve(maxVal - minVal); for (int hashIdx = 0; hashIdx < hashCount; hashIdx++) { int32 expectHashVal = hashVals[hashIdx]; PTR_ALIGN(data, sectionData, 4); tagStarts.push_back((int)(data - sectionData)); int32 hashVal = BlHash::GetTypeHash(data) % hashBucketsSize; BF_ASSERT(hashVal == expectHashVal); tagId++; } //////// Validate hash adjusters if (hashAdjSize > 0) { data = hashDataHead + hashAdjOffset; uint8* dataEnd = data + hashAdjSize; GET_INTO(int32, adjustCount); GET_INTO(int32, adjustCapacity); GET_INTO(int32, usedLen); uint32* usedBitsArr = (uint32*)data; data += usedLen * sizeof(int32); GET_INTO(int32, deletedLen); uint32* deletedBitsArr = (uint32*)data; data += deletedLen * sizeof(int32); int curAdjIdx = 0; for (int usedIdx = 0; usedIdx < usedLen; usedIdx++) { int usedBits = usedBitsArr[usedIdx]; for (int i = 0; i < 32; i++) { if ((usedBits & (1 << i)) != 0) { GET_INTO(int32, adjVal); GET_INTO(CV_typ_t, typeId); uint8* typeData = sectionData + tagStarts[typeId - minVal]; int32 hashVal = BlHash::GetTypeHash(typeData) % hashBucketsSize; const char* strVal = mStringTable.mStrTable + adjVal; int32 strHashVal = BlHash::HashStr_PdbV1(strVal) % hashBucketsSize; BF_ASSERT(hashVal == strHashVal); curAdjIdx++; } } } BF_ASSERT(curAdjIdx == adjustCount); } data = typeDataHead; } } void COFF::ParseTypeData() { if (!mPDBLoaded) return; if (mParsedTypeData) return; mParsedTypeData = true; auto linkedModule = GetLinkedModule(); int startingTypeIdx = (int)linkedModule->mTypes.size(); //uint8* sectionData; int sectionSize = 0; int hashAdjOffset = 0; int32 hashStream = -1; int32 hashAdjSize = 0; int32 dataOffset = 0; ParseTypeData(2, mCvTypeSectionReader, sectionSize, dataOffset, hashStream, hashAdjOffset, hashAdjSize, mCvMinTag, mCvMaxTag); //mCvTypeSectionData = sectionData; mCvTypeMap.Clear(); mCvTagStartMap.Clear(); mCvTypeMap.Resize(mCvMaxTag - mCvMinTag); mCvTagStartMap.Resize(mCvMaxTag - mCvMinTag); //DbgDataMap dataMap(minVal, maxVal); //uint8* data = sectionData + dataOffset; ParseTypeData(mCvTypeSectionReader, dataOffset); if (hashAdjSize > 0) { int sectionSize = 0; uint8* data = CvReadStream(hashStream, §ionSize); uint8* sectionData = data; data = sectionData + hashAdjOffset; GET_INTO(int32, adjustCount); GET_INTO(int32, unk0); GET_INTO(int32, unkCount); for (int i = 0; i < unkCount; i++) { GET_INTO(int32, unk2); } GET_INTO(int32, unkCount2); for (int i = 0; i < unkCount2; i++) { GET_INTO(int32, unk3); } // Types listed in the adjustment table are always primary types, // they should override any "old types" with the same name for (int adjIdx = 0; adjIdx < adjustCount; adjIdx++) { GET_INTO(int32, adjVal); GET_INTO(CV_typ_t, typeId); DbgType* dbgType = CvGetType(typeId); if (dbgType != NULL) { dbgType->mPriority = DbgTypePriority_Primary_Explicit; } } delete [] sectionData; } FixTypes(startingTypeIdx); MapTypes(startingTypeIdx); BF_ASSERT(mTempBufIdx == 0); } void COFF::FixConstant(DbgVariable* constVar) { if (constVar->mType->mTypeCode == DbgType_u8) constVar->mConstValue &= 0xFF; else if (constVar->mType->mTypeCode == DbgType_u16) constVar->mConstValue &= 0xFFFF; else if (constVar->mType->mTypeCode == DbgType_u32) constVar->mConstValue &= 0xFFFFFFFF; } void COFF::MapRanges(DbgVariable* variable, CV_LVAR_ADDR_RANGE* range, CV_LVAR_ADDR_GAP* gaps) { auto rangeStart = GetSectionAddr(range->isectStart, range->offStart); if (variable->mRangeStart == 0) { variable->mRangeStart = rangeStart; variable->mRangeLen = (rangeStart + range->cbRange) - variable->mRangeStart; } else { addr_target maxEnd = BF_MAX(variable->mRangeStart + variable->mRangeLen, rangeStart + range->cbRange); variable->mRangeStart = BF_MIN(variable->mRangeStart, rangeStart); variable->mRangeLen = maxEnd - variable->mRangeStart; } } void COFF::MakeThis(DbgSubprogram* curSubprogram, DbgVariable*& curParam) { if ((curParam != NULL) || (curSubprogram->mParams.mHead != NULL)) return; BP_ALLOC_T(DbgVariable); curParam = mAlloc.Alloc(); curParam->mIsParam = true; curParam->mName = "this"; curSubprogram->mParams.mHead = curParam; } static int gSymCount = 0; void COFF::ParseCompileUnit_Symbols(DbgCompileUnit* compileUnit, uint8* sectionData, uint8* data, uint8* symDataEnd, CvInlineInfoVec& inlineDataVec, bool wantDeferInternals, DbgSubprogram* useSubprogram) { bool isOptimized = false; uint8* dataHead = data; CvCompileUnit* cvCompileUnit = (CvCompileUnit*)compileUnit; uint8* symDataStart = data; DbgSubprogram* curSubprogram = NULL; DbgVariable* curRegRelVariable = NULL; DbgVariable* curParam = NULL; DbgVariable* localVar = NULL; bool inLocalVarRanged = false; SizedArray blockStack; blockStack.push_back(compileUnit->mGlobalBlock); int deferBlockDepth = 0; int deferInlineDepth = 0; uint8* newLocationDataStart = NULL; uint8* locationDataStart = NULL; uint8* locationDataEnd = NULL; int locationDataCount = 0; struct _DeferredVariableLocation { DbgVariable* mVariable; uint8* mRangedStart; int mRangedLength; _DeferredVariableLocation() { mVariable = NULL; mRangedStart = NULL; mRangedLength = 0; } }; SizedArray<_DeferredVariableLocation, 16> deferredVariableLocations; int unrangedIdx = -1; auto _NextUnrangedLocalVar = [&](const char* name, DbgType* varType) { inLocalVarRanged = false; localVar = NULL; unrangedIdx++; while (unrangedIdx < deferredVariableLocations.size()) { localVar = deferredVariableLocations[unrangedIdx].mVariable; if (strcmp(localVar->mName, name) == 0) break; localVar = NULL; unrangedIdx++; } bool isParam = false; if (localVar == NULL) { if (curParam != NULL) { isParam = true; if (curParam->mType != varType) { // Type was different, probably from a 'ref' being added when we are passing composites curParam->mName = name; curParam = curParam->mNext; } else { localVar = curParam; curParam = curParam->mNext; } } } if (localVar == NULL) { BP_ALLOC_T(DbgVariable); localVar = mAlloc.Alloc(); if (name != NULL) blockStack.back()->mVariables.PushBack(localVar); //return; } localVar->mName = name; if (isParam) localVar->mIsParam = true; if (strcmp(name, "this") == 0) { MakeThis(curSubprogram, localVar); BF_ASSERT(varType->mTypeCode == DbgType_Ptr); curSubprogram->mParentType = varType->mTypeParam; curSubprogram->mHasThis = true; } }; auto _FinishLocationData = [&]() { if (locationDataStart == NULL) return; if (inLocalVarRanged) { auto& deferredVariableLocation = deferredVariableLocations.back(); BF_ASSERT(deferredVariableLocation.mRangedLength == 0); deferredVariableLocation.mRangedStart = locationDataStart; deferredVariableLocation.mRangedLength = (int)(locationDataEnd - locationDataStart); } else { if (unrangedIdx >= deferredVariableLocations.size()) { // There was no ranged data, commit just the unranged entry BF_ASSERT(locationDataCount == localVar->mLocationLen); BP_ALLOC("DeferredVarLoc0", locationDataEnd - locationDataStart); localVar->mLocationData = mAlloc.AllocBytes(locationDataEnd - locationDataStart, "DeferredVarLoc0"); memcpy((uint8*)localVar->mLocationData, locationDataStart, locationDataEnd - locationDataStart); } else { // We have both ranged an unranged data. Merge them! auto& deferredVariableLocation = deferredVariableLocations[unrangedIdx]; auto deferredVar = deferredVariableLocation.mVariable; BF_ASSERT(deferredVar == localVar); if (deferredVariableLocation.mRangedLength == 0) BF_ASSERT(locationDataCount == localVar->mLocationLen); else BF_ASSERT(locationDataCount < localVar->mLocationLen); BP_ALLOC("DeferredVarLoc1", deferredVariableLocation.mRangedLength + (locationDataEnd - locationDataStart)); localVar->mLocationData = mAlloc.AllocBytes(deferredVariableLocation.mRangedLength + (locationDataEnd - locationDataStart), "DeferredVarLoc1"); memcpy((uint8*)localVar->mLocationData, deferredVariableLocation.mRangedStart, deferredVariableLocation.mRangedLength); memcpy((uint8*)localVar->mLocationData + deferredVariableLocation.mRangedLength, locationDataStart, locationDataEnd - locationDataStart); deferredVariableLocation.mRangedLength = -1; // Mark as 'handled' } } locationDataStart = NULL; locationDataEnd = NULL; locationDataCount = 0; }; auto _FlushDeferredVariableLocations = [&]() { for (auto& deferredVariableLocation : deferredVariableLocations) { if (deferredVariableLocation.mRangedLength == -1) continue; auto deferredVar = deferredVariableLocation.mVariable; BP_ALLOC("DeferredVarLoc2", deferredVariableLocation.mRangedLength); deferredVar->mLocationData = mAlloc.AllocBytes(deferredVariableLocation.mRangedLength, "DeferredVarLoc2"); memcpy((uint8*)deferredVar->mLocationData, deferredVariableLocation.mRangedStart, deferredVariableLocation.mRangedLength); } deferredVariableLocations.clear(); unrangedIdx = -1; }; bool inlineDebugDump = false; if (useSubprogram != NULL) { curSubprogram = useSubprogram; blockStack.push_back(&curSubprogram->mBlock); curParam = curSubprogram->mParams.mHead; curRegRelVariable = curParam; } while (data < symDataEnd) { bool deferInternals = (wantDeferInternals) || (deferInlineDepth > 0); gSymCount++; uint8* dataStart = data; GET_INTO(uint16, symLen); uint8* dataEnd = data + symLen; GET_INTO(uint16, symType); if (!mIsHotObjectFile) BF_ASSERT(symLen % 4 == 2); bool newLocalVarHasLocData = false; DbgVariable* prevLocalVar = localVar; switch (symType) { case S_LOCAL: case S_BPREL32: case S_REGISTER: case S_REGREL32: case S_GPROC32: case S_LPROC32: case S_GPROC32_ID: case S_LPROC32_ID: // Starting a new var or method if ((!deferInternals) && (locationDataStart != NULL)) { _FinishLocationData(); } break; } /*if (handled) { data = dataEnd; continue; }*/ if (inlineDebugDump) BfLogCv("SYMTYPE: %X\n", symType); switch (symType) { case S_UDT: { UDTSYM& udtSym = *(UDTSYM*)dataStart; DbgType* typeDefType = CvCreateType(); typeDefType->mTypeCode = DbgType_TypeDef; typeDefType->mTypeParam = CvGetType(udtSym.typind); typeDefType->mName = DbgDupString((const char*)udtSym.name, "DbgDupString.S_UDT"); if (strncmp(typeDefType->mName, "_bf::", 5) == 0) { typeDefType->mLanguage = DbgLanguage_Beef; typeDefType->mName += 5; } else { typeDefType->mLanguage = DbgLanguage_C; } typeDefType->mTypeName = typeDefType->mName; } break; case S_OBJNAME: { GET_INTO(int32, objSig); const char* objName = CvParseAndDupString(data); } break; case S_CONSTANT: { CONSTSYM& constSym = *(CONSTSYM*)dataStart; BP_ALLOC_T(DbgVariable); DbgVariable* constVar = mAlloc.Alloc(); constVar->mName = DbgDupString((const char*)constSym.name, "DbgDupString.S_CONSTANT"); constVar->mType = CvGetType(constSym.typind); constVar->mIsConst = true; constVar->mIsStatic = true; constVar->mConstValue = constSym.value; data = constSym.name + strlen((const char*)constSym.name) + 1; constVar->mConstValue = CvParseConstant(constSym.value, data); FixConstant(constVar); if (constVar->mName != NULL) blockStack.back()->mVariables.PushBack(constVar); } break; case S_EXPORT: { GET_INTO(int16, ord); struct ExpFlags { unsigned short fConstant : 1; // CONSTANT unsigned short fData : 1; // DATA unsigned short fPrivate : 1; // PRIVATE unsigned short fNoName : 1; // NONAME unsigned short fOrdinal : 1; // Ordinal was explicitly assigned unsigned short fForwarder : 1; // This is a forwarder unsigned short reserved : 10; // Reserved. Must be zero. }; GET_INTO(ExpFlags, flags); const char* symName = CvParseAndDupString(data); } break; case S_GDATA32: case S_LDATA32: // In PDB reads we get this from the symbol stream if ((mIsHotObjectFile) || (curSubprogram != NULL)) { auto linkedModule = GetLinkedModule(); DATASYM32& dataSym = *(DATASYM32*)dataStart; char* name = (char*)dataSym.name; char* targetName = NULL; DbgType* targetType = mMasterCompileUnit->mGlobalType; bool overrideBeef = false; char* lastDblColon = (char*)GetLastDoubleColon(name); if (lastDblColon != NULL) { targetName = name; name = lastDblColon + 2; *lastDblColon = 0; if (strcmp(targetName, "_bf") == 0) { overrideBeef = true; } else { targetType = CvGetTypeOrNamespace(targetName); } } DbgType* dbgType = CvGetType(dataSym.typind); BP_ALLOC_T(DbgVariable); DbgVariable* variable = mAlloc.Alloc(); variable->mType = dbgType; variable->mLocationData = dataStart; variable->mLocationLen = 1; variable->mIsStatic = true; variable->mCompileUnit = mMasterCompileUnit; variable->mName = name; if (targetType == mMasterCompileUnit->mGlobalType) variable->mLinkName = name; variable->mIsExtern = symType == S_GDATA32; if (curSubprogram != NULL) { if (!mIsHotObjectFile) { // Copy this, we free the source data variable->mName = DbgDupString(name, "DbgDupString.S_GDATA32"); BP_ALLOC_T(DATASYM32) auto newPtr = mAlloc.Alloc(); memcpy(newPtr, variable->mLocationData, sizeof(DATASYM32)); variable->mLocationData = (uint8*)newPtr; } if (variable->mName != NULL) blockStack.back()->mVariables.PushBack(variable); break; } //variable->mCompileUnit = m; // Push front so we will find before the original static declaration (that has no memory associated with it) targetType->mMemberList.PushFront(variable); if ((variable->mIsExtern) && (variable->mLinkName != NULL)) mStaticVariables.push_back(variable); } break; case S_GTHREAD32: case S_LTHREAD32: { if ((mIsHotObjectFile) || (curSubprogram != NULL)) { auto linkedModule = GetLinkedModule(); THREADSYM32& dataSym = *(THREADSYM32*)dataStart; char* name = (char*)dataSym.name; char* targetName = NULL; DbgType* targetType = mMasterCompileUnit->mGlobalType; bool overrideBeef = false; char* lastDblColon = (char*)GetLastDoubleColon(name); if (lastDblColon != NULL) { targetName = name; name = lastDblColon + 2; *lastDblColon = 0; if (strcmp(targetName, "_bf") == 0) { overrideBeef = true; } else { targetType = CvGetTypeOrNamespace(targetName); } } DbgType* dbgType = CvGetType(dataSym.typind); BP_ALLOC_T(DbgVariable); DbgVariable* variable = mAlloc.Alloc(); variable->mType = dbgType; variable->mLocationData = dataStart; variable->mLocationLen = 1; variable->mIsStatic = true; variable->mCompileUnit = mMasterCompileUnit; variable->mName = name; if (targetType == mMasterCompileUnit->mGlobalType) variable->mLinkName = name; variable->mIsExtern = symType == S_GTHREAD32; if (curSubprogram != NULL) { if (!mIsHotObjectFile) { // Copy this, we free the source data variable->mName = DbgDupString(name, "DbgDupString.S_GTHREAD32"); BP_ALLOC_T(DATASYM32) auto newPtr = mAlloc.Alloc(); memcpy(newPtr, variable->mLocationData, sizeof(DATASYM32)); variable->mLocationData = (uint8*)newPtr; } if (variable->mName != NULL) blockStack.back()->mVariables.PushBack(variable); break; } //variable->mCompileUnit = m; // Push front so we will find before the original static declaration (that has no memory associated with it) targetType->mMemberList.PushFront(variable); if ((variable->mIsExtern) && (variable->mLinkName != NULL)) mStaticVariables.push_back(variable); } } break; case S_GPROC32: case S_LPROC32: case S_GPROC32_ID: case S_LPROC32_ID: { BF_ASSERT(curSubprogram == NULL); PROCSYM32* procSym = (PROCSYM32*)dataStart; DbgType* parentType = NULL; auto addr = GetSectionAddr(procSym->seg, procSym->off); DbgSubprogram* subprogram; if (procSym->typind == 0) { BP_ALLOC_T(DbgSubprogram); subprogram = mAlloc.Alloc(); } else { bool ipi = false; if ((!mIsHotObjectFile) && ((symType == S_GPROC32_ID) || (symType == S_LPROC32_ID))) { if (!mCvIPIReader.IsSetup()) CvParseIPI(); ipi = true; } subprogram = CvParseMethod(parentType, NULL, procSym->typind, ipi); } subprogram->mTagIdx = (int)(dataEnd - sectionData); // Position for method data subprogram->mIsOptimized = isOptimized; subprogram->mCompileUnit = compileUnit; const char* name = DbgDupString((const char*)procSym->name, "DbgDupString.S_GPROC32"); if (procSym->flags.CV_PFLAG_OPTDBGINFO) { subprogram->mIsOptimized = true; } if ((name[0] == '_') && (name[1] == '_')) { // This matches things like "__chkstk" or other system (supposedly) functions subprogram->mIsStepFilteredDefault = true; } if (strncmp(name, "_bf::", 5) == 0) { compileUnit->mLanguage = DbgLanguage_Beef; } localVar = NULL; bool hasColon = false; for (const char* cPtr = name; *cPtr != 0; cPtr++) hasColon |= *cPtr == ':'; subprogram->mPrologueSize = procSym->DbgStart; subprogram->mBlock.mLowPC = addr; subprogram->mBlock.mHighPC = subprogram->mBlock.mLowPC + (int32)procSym->len; BF_ASSERT(procSym->len >= 0); MapSubprogram(subprogram); curSubprogram = subprogram; curParam = subprogram->mParams.mHead; curRegRelVariable = curParam; blockStack.push_back(&subprogram->mBlock); //OutputDebugStrF("Func: %s\n", subprogram->mName); if (hasColon) { subprogram->mName = name; subprogram->mHasQualifiedName = true; compileUnit->mOrphanMethods.PushBack(curSubprogram); } else { subprogram->mName = name; compileUnit->mGlobalType->mMethodList.PushBack(curSubprogram); } BF_ASSERT(unrangedIdx == -1); } break; case S_THUNK32: { THUNKSYM32& thunkSym = *(THUNKSYM32*)dataStart; DbgType* parentType = NULL; DbgSubprogram* subprogram; BP_ALLOC_T(DbgSubprogram); subprogram = mAlloc.Alloc(); subprogram->mCompileUnit = compileUnit; subprogram->mHasQualifiedName = true; subprogram->mName = DbgDupString((const char*)thunkSym.name, "DbgDupString.S_THUNK32"); subprogram->mTagIdx = (int)(dataEnd - sectionData); // Position for method data subprogram->mBlock.mLowPC = GetSectionAddr(thunkSym.seg, thunkSym.off); subprogram->mBlock.mHighPC = subprogram->mBlock.mLowPC + thunkSym.len; BF_ASSERT(thunkSym.len >= 0); MapSubprogram(subprogram); curSubprogram = subprogram; curParam = subprogram->mParams.mHead; blockStack.push_back(&subprogram->mBlock); BF_ASSERT(unrangedIdx == -1); //OutputDebugStrF("Func: %s\n", subprogram->mName); } break; case S_BLOCK32: { if (deferInternals) { deferBlockDepth++; break; } BP_ALLOC_T(DbgBlock); DbgBlock* block = mAlloc.Alloc(); blockStack.back()->mSubBlocks.PushBack(block); blockStack.push_back(block); BLOCKSYM32* blockSym = (BLOCKSYM32*)dataStart; block->mLowPC = GetSectionAddr(blockSym->seg, blockSym->off); block->mHighPC = block->mLowPC + blockSym->len; } break; case S_FRAMEPROC: { FRAMEPROCSYM* frameProc = (FRAMEPROCSYM*)dataStart; if (curSubprogram != NULL) { // if ((curSubprogram->mName != NULL) && (strcmp(curSubprogram->mName, "_bf::IDETest::HotTester::TestFuncs") == 0)) // { // NOP; // } curSubprogram->mFrameBaseLen = frameProc->cbFrame + frameProc->cbSaveRegs; curSubprogram->mLocalBaseReg = (DbgSubprogram::LocalBaseRegKind)(uint8)frameProc->flags.encodedLocalBasePointer; } } break; case S_BPREL32: { if (deferInternals) break; BPRELSYM32* bpRel32 = (BPRELSYM32*)dataStart; const char* name = DbgDupString((const char*)bpRel32->name, "DbgDupString.S_BPREL32"); DbgType* varType = CvGetType(bpRel32->typind); _NextUnrangedLocalVar(name, varType); localVar->mName = name; localVar->mCompileUnit = compileUnit; localVar->mType = varType; // This is location data now, not just a S_LOCAL opener //prevLocalVar = localVar; newLocalVarHasLocData = true; } break; case S_REGISTER: { if (deferInternals) break; REGSYM* regSym = (REGSYM*)dataStart; const char* name = DbgDupString((const char*)regSym->name); DbgType* varType = CvGetType(regSym->typind); _NextUnrangedLocalVar(name, varType); localVar->mName = name; localVar->mCompileUnit = compileUnit; localVar->mType = varType; // This is location data now, not just a S_LOCAL opener //prevLocalVar = localVar; newLocalVarHasLocData = true; } break; case S_REGREL32: { if (deferInternals) break; REGREL32* regRel32 = (REGREL32*)dataStart; const char* name = DbgDupString((const char*)regRel32->name); DbgType* varType = CvGetType(regRel32->typind); _NextUnrangedLocalVar(name, varType); localVar->mName = name; localVar->mCompileUnit = compileUnit; if ((localVar->mType != NULL) && (!localVar->mType->IsPointer()) && (varType->IsPointer())) localVar->mSigNoPointer = true; localVar->mType = varType; // This is location data now, not just a S_LOCAL opener //prevLocalVar = localVar; newLocalVarHasLocData = true; } break; case S_LOCAL: { if (deferInternals) break; inLocalVarRanged = true; LOCALSYM& localSym = *(LOCALSYM*)dataStart; char* name = DbgDupString((const char*)localSym.name); bool isConst = false; int64 constVal = 0; if ((compileUnit->mLanguage == DbgLanguage_Beef) && (name[0] != '$')) { for (char* cPtr = name + 1; true; cPtr++) { char c = *cPtr; if (c == 0) break; if (c == '$') { if (cPtr[1] == '_') { isConst = true; constVal = -atoll(cPtr + 2); *cPtr = 0; } else if ((cPtr[1] >= '0') && (cPtr[1] <= '9')) { isConst = true; constVal = atoll(cPtr + 1); *cPtr = 0; } } } } if ((name != NULL) && (name[0] == '#')) { if (strcmp(name + 1, "StepOver") == 0) { curSubprogram->mIsStepFilteredDefault = true; } localVar = NULL; break; } DbgType* varType = CvGetType(localSym.typind, cvCompileUnit); if (name != NULL) { if ((localSym.flags.fIsOptimizedOut) && (name[0] != '$')) { if (varType->mSize > 0) curSubprogram->mIsOptimized = true; } if ((strcmp(name, "this") == 0) && (varType != NULL)) { //BF_ASSERT(varType->mTypeCode == DbgType_Ptr); MakeThis(curSubprogram, curParam); curSubprogram->mParentType = varType->mTypeParam; curSubprogram->mHasThis = true; } } bool handledLocalVar = false; if (curParam != NULL) { if ((name != NULL) && (name[0] == '$')) { int strLen = strlen(name); // Splat head const char* dollarPos = strchr(name + 1, '$'); const char* nameStr = name + 1; int nameLen = dollarPos - name - 1; if ((dollarPos != NULL) && ((localVar == NULL) || (strncmp(localVar->mName, name, nameLen + 1) != 0))) { BP_ALLOC("ParamName", nameLen + 1); char* dupStr = (char*)mAlloc.AllocBytes(nameLen + 1, "ParamName"); memcpy(dupStr, nameStr, nameLen); curParam->mName = dupStr; curParam->mIsConst = true; curParam = curParam->mNext; if (strcmp(dupStr, "this") == 0) curSubprogram->mHasThis = true; } } else if ((curParam->mType != varType) && (varType->mTypeCode != DbgType_Void)) { // Type was different, probably from a 'ref' being added when we are passing composites curParam->mName = name; curParam = curParam->mNext; } else { localVar = curParam; curParam = curParam->mNext; handledLocalVar = true; } } if (!handledLocalVar) { BP_ALLOC_T(DbgVariable); localVar = mAlloc.Alloc(); if (name != NULL) blockStack.back()->mVariables.PushBack(localVar); } localVar->mName = name; localVar->mCompileUnit = compileUnit; localVar->mType = varType; localVar->mIsConst = isConst; localVar->mConstValue = constVal; _DeferredVariableLocation deferredLocation; deferredLocation.mVariable = localVar; deferredVariableLocations.Add(deferredLocation); } break; case S_DEFRANGE_REGISTER: { if (deferInternals) break; DEFRANGESYMREGISTER& defRangeReg = *(DEFRANGESYMREGISTER*)dataStart; if (localVar != NULL) MapRanges(localVar, &defRangeReg.range, defRangeReg.gaps); } break; case S_DEFRANGE_FRAMEPOINTER_REL: { if (deferInternals) break; DEFRANGESYMFRAMEPOINTERREL& defRangeFPRel = *(DEFRANGESYMFRAMEPOINTERREL*)dataStart; MapRanges(localVar, &defRangeFPRel.range, defRangeFPRel.gaps); } break; case S_DEFRANGE_SUBFIELD_REGISTER: { if (deferInternals) break; DEFRANGESYMSUBFIELDREGISTER& defRangeSubFieldReg = *(DEFRANGESYMSUBFIELDREGISTER*)dataStart; } break; case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: { if (deferInternals) break; DEFRANGESYMFRAMEPOINTERREL_FULL_SCOPE& defRangeFPRel = *(DEFRANGESYMFRAMEPOINTERREL_FULL_SCOPE*)dataStart; } break; case S_DEFRANGE_REGISTER_REL: { if (deferInternals) break; DEFRANGESYMREGISTERREL& defRangeRegRel = *(DEFRANGESYMREGISTERREL*)dataStart; if (localVar != NULL) MapRanges(localVar, &defRangeRegRel.range, defRangeRegRel.gaps); } break; case S_ENDARG: if (deferInternals) break; BF_ASSERT(curParam == NULL); break; case S_END: case S_PROC_ID_END: if (deferBlockDepth > 0) --deferBlockDepth; else blockStack.pop_back(); BF_ASSERT(blockStack.size() > 0); if (blockStack.size() == 1) { _FinishLocationData(); _FlushDeferredVariableLocations(); // Done with forced subprogram? if (useSubprogram != NULL) { return; } else if (deferInternals) { if (curSubprogram->mTagIdx != -1) { int endOffset = (int)(dataEnd - sectionData); curSubprogram->mDeferredInternalsSize = endOffset - curSubprogram->mTagIdx; } } inlineDebugDump = false; curSubprogram = NULL; curParam = NULL; } break; case S_COMPILE2: { COMPILESYM* compileSym = (COMPILESYM*)dataStart; } break; case S_COMPILE3: { COMPILESYM3* compileSym = (COMPILESYM3*)dataStart; } break; case S_ENVBLOCK: { GET_INTO(uint8, flags); while (true) { const char* envKey = CvParseAndDupString(data); if (envKey == NULL) break; const char* envValue = CvParseAndDupString(data); } } break; case S_BUILDINFO: { CV_ItemId buildInfoId = GET(CV_ItemId); } break; case S_TRAMPOLINE: break; case S_COFFGROUP: break; case S_SECTION: break; case S_FRAMECOOKIE: { FRAMECOOKIE& frameCookie = *(FRAMECOOKIE*)dataStart; } break; case S_LABEL32: { LABELSYM32& labelSym = *(LABELSYM32*)dataStart; } break; case S_CALLSITEINFO: { CALLSITEINFO& callSiteInfo = *(CALLSITEINFO*)dataStart; } break; case S_HEAPALLOCSITE: { HEAPALLOCSITE& heapAllocSite = *(HEAPALLOCSITE*)dataStart; } break; case S_FILESTATIC: { FILESTATICSYM& fileStaticSym = *(FILESTATICSYM*)dataStart; } break; case S_CALLEES: { FUNCTIONLIST& calleeList = *(FUNCTIONLIST*)dataStart; } break; case S_CALLERS: { FUNCTIONLIST& calleeList = *(FUNCTIONLIST*)dataStart; } break; case S_POGODATA: { POGOINFO& pogoInfo = *(POGOINFO*)dataStart; } break; case S_INLINESITE: case S_INLINESITE2: { if (useSubprogram != NULL) { deferInlineDepth++; break; // Already handled this } uint32 inlinee; uint8* binaryAnnotations; if (symType == S_INLINESITE) { INLINESITESYM& inlineSite = *(INLINESITESYM*)dataStart; inlinee = inlineSite.inlinee; binaryAnnotations = (uint8*)&inlineSite.binaryAnnotations; } else // S_INLINESITE2 { INLINESITESYM2& inlineSite = *(INLINESITESYM2*)dataStart; inlinee = inlineSite.inlinee; binaryAnnotations = (uint8*)&inlineSite.binaryAnnotations; } DbgSubprogram* inlineParent = curSubprogram; DbgSubprogram* subprogram = NULL; if (mIsHotObjectFile) { subprogram = CvParseMethod(NULL, NULL, inlinee, false); subprogram->mCompileUnit = compileUnit; curSubprogram = subprogram; } else if ((inlinee & 0x80000000) != 0) { BP_ALLOC_T(DbgSubprogram); subprogram = mAlloc.Alloc(); // ?? subprogram->mCompileUnit = compileUnit; if (!mCvIPIReader.IsSetup()) CvParseIPI(); curSubprogram = subprogram; } else { BP_ALLOC_T(DbgSubprogram); subprogram = mAlloc.Alloc(); subprogram->mCompileUnit = compileUnit; curSubprogram = subprogram; FixupInlinee(curSubprogram, inlinee); } BF_ASSERT(subprogram == curSubprogram); curSubprogram->mTagIdx = (int)(dataEnd - sectionData); // Position for method data BP_ALLOC_T(DbgInlineeInfo); curSubprogram->mInlineeInfo = mAlloc.Alloc(); curSubprogram->mInlineeInfo->mInlineParent = inlineParent; if (inlineParent->mInlineeInfo != NULL) curSubprogram->mInlineeInfo->mInlineDepth = inlineParent->mInlineeInfo->mInlineDepth + 1; else curSubprogram->mInlineeInfo->mInlineDepth = 1; curSubprogram->mInlineeInfo->mRootInliner = inlineParent->GetRootInlineParent(); curSubprogram->mInlineeInfo->mInlineeId = 0; curSubprogram->mFrameBaseData = inlineParent->mFrameBaseData; curSubprogram->mFrameBaseLen = inlineParent->mFrameBaseLen; curSubprogram->mLocalBaseReg = inlineParent->mLocalBaseReg; if (curSubprogram->mName == NULL) { /*curSubprogram->mName = ""; String name = StrFormat("@%@", curSubprogram); curSubprogram->mName = DbgDupString(name.c_str());*/ String name = StrFormat("@%@", curSubprogram); curSubprogram->mName = DbgDupString(name.c_str()); curSubprogram->mInlineeInfo->mInlineeId = inlinee; } if (inlineDebugDump) { int depth = curSubprogram->GetInlineDepth(); BfLogCv("S_INLINESITE Ofs:%d Depth:%d Inlinee:%X Subprogram:%@ %s\n Parent:%@ %s\n", (dataStart - symDataStart), depth, inlinee, curSubprogram, curSubprogram->mName, curSubprogram->mInlineeInfo->mInlineParent, curSubprogram->mInlineeInfo->mInlineParent->mName); } CvInlineInfo inlineInfo; inlineInfo.mNext = NULL; inlineInfo.mTail = NULL; inlineInfo.mInlinee = inlinee; inlineInfo.mSubprogram = NULL; inlineInfo.mSubprogram = curSubprogram; inlineInfo.mData = binaryAnnotations; inlineInfo.mDataLen = dataEnd - inlineInfo.mData; inlineInfo.mDebugDump = inlineDebugDump; inlineDataVec.push_back(inlineInfo); blockStack.push_back(&curSubprogram->mBlock); } break; case S_INLINESITE_END: { if (inlineDebugDump) BfLogCv("S_INLINESITE_END\n"); if (deferInlineDepth > 0) { deferInlineDepth--; } else { if (deferInternals) { int endOffset = (int)(dataEnd - sectionData); curSubprogram->mDeferredInternalsSize = endOffset - curSubprogram->mTagIdx; } curSubprogram = curSubprogram->mInlineeInfo->mInlineParent; blockStack.pop_back(); } } break; case S_SSEARCH: { SEARCHSYM* searchSym = (SEARCHSYM*)dataStart; } break; case S_SEPCODE: { BP_ALLOC_T(DbgSubprogram); curSubprogram = mAlloc.Alloc(); curSubprogram->mCompileUnit = compileUnit; blockStack.push_back(&curSubprogram->mBlock); SEPCODESYM* sepCodeSym = (SEPCODESYM*)dataStart; auto addr = GetSectionAddr(sepCodeSym->sect, sepCodeSym->off); auto parentAddr = GetSectionAddr(sepCodeSym->sectParent, sepCodeSym->offParent); curSubprogram->mBlock.mLowPC = addr; curSubprogram->mBlock.mHighPC = addr + sepCodeSym->length; String name = StrFormat("SEPCODE@%p", parentAddr); curSubprogram->mName = DbgDupString(name.c_str()); curSubprogram->mTagIdx = (int)(dataEnd - sectionData); // Position for method data MapSubprogram(curSubprogram); } break; case S_COMPILE: break; case S_ANNOTATION: break; case S_UNAMESPACE: break; case /*S_FASTLINK*/0x1167: break; case /*S_INLINEES*/0x1168: break; case 0x1176: break; default: BF_FATAL("Unhandled"); break; } bool hasNewLocalVar = (localVar != prevLocalVar) && (localVar != NULL); if (newLocalVarHasLocData) { prevLocalVar = localVar; } if (deferInlineDepth == 0) { switch (symType) { case S_BPREL32: case S_REGISTER: case S_REGREL32: case S_DEFRANGE_REGISTER: case S_DEFRANGE_FRAMEPOINTER_REL: case S_DEFRANGE_SUBFIELD_REGISTER: case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: case S_DEFRANGE_REGISTER_REL: { if (prevLocalVar != NULL) { /*if (localVar->mLocationData == NULL) localVar->mLocationData = dataStart; localVar->mLocationLen++;*/ if (locationDataStart == NULL) locationDataStart = dataStart; locationDataEnd = dataEnd; locationDataCount++; localVar->mLocationLen++; } } break; } } if (hasNewLocalVar) { if (localVar->mName != NULL) { // This $alias$ hack is unfortunate, but LLVM gets confused when we attempt to tie multiple debug variables // to the same memory location, which can happen during mixin injection. The result is that the mixin gets // some premature instructions attributed to it from the variable declaration. This fixes that. if (strncmp(localVar->mName, "$alias$", 7) == 0) { localVar->mName += 7; char* dollarPos = (char*)strchr(localVar->mName, '$'); if (dollarPos != 0) { *dollarPos = 0; const char* findName = dollarPos + 1; bool found = false; for (int blockIdx = (int)blockStack.size() - 1; blockIdx >= 0; blockIdx--) { DbgBlock* block = blockStack[blockIdx]; for (auto checkVar : block->mVariables) { if (strcmp(checkVar->mName, findName) == 0) { localVar->mConstValue = checkVar->mConstValue; localVar->mType = checkVar->mType; localVar->mLocationData = checkVar->mLocationData; localVar->mLocationLen = checkVar->mLocationLen; found = true; break; } } if (found) break; } } } if (compileUnit->mLanguage != DbgLanguage_Beef) { for (char* cPtr = (char*)localVar->mName; true; cPtr++) { char c = *cPtr; if (c == 0) break; if ((c == '<') || (c == '>')) *cPtr = '$'; } } } } /*if (prevLocalVar != NULL) { BF_ASSERT(locationDataCount == prevLocalVar->mLocationLen); }*/ data = dataEnd; //PTR_ALIGN(data, sectionData, 4); } if (localVar != NULL) { if (locationDataStart != NULL) { _FinishLocationData(); } } _FlushDeferredVariableLocations(); } CvCompileUnit* COFF::ParseCompileUnit(CvModuleInfo* moduleInfo, CvCompileUnit* compileUnit, uint8* sectionData, int sectionSize) { BP_ZONE("COFF::ParseCompileUnit"); CvInlineInfoVec inlineDataVec; Dictionary inlineDataDict; if (moduleInfo != NULL) { BfLogCv("ParseCompileUnit %s\n", moduleInfo->mModuleName); } else { BfLogCv("ParseCompileUnit NULL\n"); } int allocSizeStart = mAlloc.GetAllocSize(); if (compileUnit == NULL) { compileUnit = new CvCompileUnit(this); mCompileUnits.push_back(compileUnit); } compileUnit->mDbgModule = this; if (moduleInfo != NULL) { compileUnit->mModuleIdx = moduleInfo->mIdx; compileUnit->mName = moduleInfo->mModuleName; moduleInfo->mCompileUnit = compileUnit; } else { compileUnit->mModuleIdx = NULL; compileUnit->mName = mFilePath.c_str(); } uint8* data = sectionData; uint8* dataEnd = NULL; GET_INTO(uint32, infoType); BF_ASSERT(infoType == CV_SIGNATURE_C13); int taggedSize = sectionSize; uint8* debugSubSectionsStart = data; if (moduleInfo != NULL) { taggedSize = moduleInfo->mLinesBytes; debugSubSectionsStart = data + moduleInfo->mSymbolBytes - sizeof(uint32); } else { taggedSize = sectionSize - sizeof(uint32); } if (mStringTable.mStrTable == NULL) { data = debugSubSectionsStart; dataEnd = data + taggedSize; while (data < dataEnd) { PTR_ALIGN(data, sectionData, 4); GET_INTO(int32, lineInfoType); GET_INTO(int32, lineInfoLength); uint8* dataStart = data; uint8* dataEnd = data + lineInfoLength; if (lineInfoType == DEBUG_S_STRINGTABLE) { if (mStringTable.mStrTable == NULL) mStringTable.mStrTable = (const char*)data; } data = dataEnd; } } // { data = debugSubSectionsStart; dataEnd = data + taggedSize; while (data < dataEnd) { PTR_ALIGN(data, sectionData, 4); GET_INTO(int32, lineInfoType); GET_INTO(int32, lineInfoLength); uint8* dataStart = data; uint8* dataEnd = data + lineInfoLength; if (lineInfoType == DEBUG_S_CROSSSCOPEIMPORTS) { while (data < dataEnd) { compileUnit->mImports.Add(CvCrossScopeImport()); auto& crossScopeImport = compileUnit->mImports.back(); crossScopeImport.mScopeCompileUnit = NULL; GET_INTO(uint32, externalScope); const char* fileName = mStringTable.mStrTable + externalScope; BfLogCv("DEBUG_S_CROSSSCOPEIMPORTS Scope:%s ", fileName); crossScopeImport.mScopeName = fileName; // /*for (int checkIdx = 0; checkIdx < (int)mCvModuleInfo.size(); checkIdx++) { auto checkModuleInfo = mCvModuleInfo[checkIdx]; if ((checkModuleInfo != NULL) && (checkModuleInfo->mModuleName != NULL) && (stricmp(checkModuleInfo->mModuleName, fileName) == 0)) { ParseCompileUnit(checkIdx); } }*/ GET_INTO(int32, numRefs); for (int i = 0; i < numRefs; i++) { GET_INTO(uint32, refId); BfLogCv(" %X", refId); crossScopeImport.mImports.Add(refId); } BfLogCv("\n"); } } else if (lineInfoType == DEBUG_S_CROSSSCOPEEXPORTS) { BfLogCv("DEBUG_S_CROSSSCOPEEXPORTS"); while (data < dataEnd) { GET_INTO(int32, localId); GET_INTO(int32, globalId); BfLogCv(" %X=%X", globalId, localId); CvCrossScopeExportEntry entry; entry.mLocalId = localId; entry.mGlobalId = globalId; compileUnit->mExports.Add(entry); } BfLogCv("\n"); } else if (lineInfoType == DEBUG_S_FRAMEDATA) { uint8* dataPtr = data; addr_target baseAddr = 0; int relAddr = GET_FROM(dataPtr, int32); ParseFrameDescriptors(dataPtr, dataEnd - dataPtr, GetLinkedModule()->mImageBase + relAddr); } data = dataEnd; } } if (moduleInfo != NULL) { data = sectionData + 4; dataEnd = data + moduleInfo->mSymbolBytes - sizeof(uint32); bool wantsDeferInternals = true; ParseCompileUnit_Symbols(compileUnit, sectionData, data, dataEnd, inlineDataVec, wantsDeferInternals, NULL); //ParseCompileUnit_Symbols(compileUnit, sectionData, data, dataEnd, inlineDataVec, false, NULL); data = dataEnd; } // Scan debug subsections to find file checksum table, and to load symbol data for hotloads Dictionary checksumFileRefs; { BP_ZONE("ParseCompileUnit_SrcFiles"); data = debugSubSectionsStart; dataEnd = data + taggedSize; while (true) { PTR_ALIGN(data, sectionData, 4); if (data >= dataEnd) break; GET_INTO(int32, lineInfoType); GET_INTO(int32, lineInfoLength); uint8* dataStart = data; uint8* dataEnd = data + lineInfoLength; if (lineInfoType < 0) { // Ignore } else if (lineInfoType == DEBUG_S_SYMBOLS) { ParseCompileUnit_Symbols(compileUnit, sectionData, data, dataEnd, inlineDataVec, false, NULL); } else if (lineInfoType == DEBUG_S_FILECHKSMS) { BF_ASSERT(checksumFileRefs.size() == 0); while (data < dataEnd) { int dataOfs = (int)(data - dataStart); GET_INTO(uint, fileTableOfs); const char* fileName = mStringTable.mStrTable + fileTableOfs; DbgSrcFile* srcFile = NULL; if ((fileName[0] == '/') || (fileName[0] == '\\') || ((fileName[0] != 0) && (fileName[1] == ':'))) { srcFile = AddSrcFile(compileUnit, fileName); } else { String fullDir = GetFileDir(mFilePath); fullDir.Append("\\"); fullDir.Append(fileName); srcFile = AddSrcFile(compileUnit, fullDir); } if (srcFile->IsBeef()) compileUnit->mLanguage = DbgLanguage_Beef; GET_INTO(uint8, hashLen); GET_INTO(uint8, hashType); if ((hashType == 1) && (hashLen == 16)) { srcFile->mHashKind = DbgHashKind_MD5; memcpy(srcFile->mHash, data, 16); } else if ((hashType == 3) && (hashLen == 32)) { srcFile->mHashKind = DbgHashKind_SHA256; memcpy(srcFile->mHash, data, 32); } data += hashLen; checksumFileRefs.TryAdd(dataOfs, (int)compileUnit->mSrcFileRefs.size() - 1); PTR_ALIGN(data, sectionData, 4); } } else { BF_ASSERT((lineInfoType >= DEBUG_S_SYMBOLS) && (lineInfoType <= DEBUG_S_COFF_SYMBOL_RVA)); } data = dataEnd; } } if (!inlineDataVec.IsEmpty()) { for (auto& inlineData : inlineDataVec) { uint32* keyPtr; CvInlineInfo** valuePtr; if (inlineDataDict.TryAdd(inlineData.mInlinee, &keyPtr, &valuePtr)) { inlineData.mTail = &inlineData; *valuePtr = &inlineData; } else { (*valuePtr)->mTail->mNext = &inlineData; (*valuePtr)->mTail = &inlineData; } } } int inlineDataIdx = 0; DbgLineDataBuilder lineBuilder(this); // { BP_ZONE("ParseCompileUnit_LineInfo"); //int totalLineCount = 0; //int targetLineCount = 0; int inlineIdx = 0; // struct _InlinerData // { // bool mWantSummaryDump; // Array mInlinees; // Array mInlineLineData; // // _InlinerData() // { // mWantSummaryDump = false; // } // }; // // Dictionary deferredInlinerDatas; // Line info data = debugSubSectionsStart; dataEnd = data + taggedSize; while (data < dataEnd) { PTR_ALIGN(data, sectionData, 4); GET_INTO(int32, lineInfoType); GET_INTO(int32, lineInfoLength); uint8* dataStart = data; uint8* dataEnd = data + lineInfoLength; if (lineInfoType == DEBUG_S_FILECHKSMS) { // Already handled } else if (lineInfoType == DEBUG_S_LINES) { CV_DebugSLinesHeader_t& lineSec = GET(CV_DebugSLinesHeader_t); Array lineDataVec; addr_target contribStartAddr = GetSectionAddr(lineSec.segCon, lineSec.offCon); while (data < dataEnd) { lineDataVec.Clear(); CV_DebugSLinesFileBlockHeader_t& linesFileHeader = GET(CV_DebugSLinesFileBlockHeader_t); DbgSrcFileReference* srcFileRef = &compileUnit->mSrcFileRefs[checksumFileRefs[linesFileHeader.offFile]]; bool isBeef = srcFileRef->mSrcFile->IsBeef(); int expectSize = (int)sizeof(CV_DebugSLinesFileBlockHeader_t) + (int)(linesFileHeader.nLines * sizeof(CV_Line_t)); if ((lineSec.flags & CV_LINES_HAVE_COLUMNS) != 0) expectSize += (int)(linesFileHeader.nLines * sizeof(int16) * 2); BF_ASSERT(expectSize == linesFileHeader.cbBlock); lineDataVec.Resize(linesFileHeader.nLines); for (int lineIdx = 0; lineIdx < linesFileHeader.nLines; lineIdx++) { CV_Line_t& srcLineData = GET(CV_Line_t); DbgLineData& lineData = lineDataVec[lineIdx]; lineData.mRelAddress = (uint32)((contribStartAddr + srcLineData.offset) - mImageBase); if (srcLineData.linenumStart == 0xf00f00) // Never step into { if (lineIdx > 0) lineData.mLine = lineDataVec[lineIdx - 1].mLine; lineData.mColumn = -2; // Never step into marker } else if (srcLineData.linenumStart == 0xfeefee) // Always step into { if (lineIdx > 0) lineData.mLine = lineDataVec[lineIdx - 1].mLine; lineData.mColumn = -1; // Always step into marker } else lineData.mLine = srcLineData.linenumStart - 1; // In Beef we always set the column, so a section without CV_LINES_HAVE_COLUMNS indicates a block of invalid // positions that we want to skip over if (isBeef) lineData.mColumn = -1; } if ((lineSec.flags & CV_LINES_HAVE_COLUMNS) != 0) { for (int lineIdx = 0; lineIdx < linesFileHeader.nLines; lineIdx++) { CV_Column_t& srcColumnData = GET(CV_Column_t); DbgLineData& lineData = lineDataVec[lineIdx]; lineData.mColumn = (int)srcColumnData.offColumnStart - 1; } } DbgLineData* lastLineData = NULL; for (int lineIdx = 0; lineIdx < linesFileHeader.nLines; lineIdx++) { DbgLineData& lineData = lineDataVec[lineIdx]; lastLineData = lineBuilder.Add(compileUnit, lineData, srcFileRef->mSrcFile, NULL); } } } else if (lineInfoType == DEBUG_S_INLINEELINES) { int linesType = GET(int); while (data < dataEnd) { int inlinee = 0; int srcFileIdx = -1; int startLine; DbgSrcFileReference* startSrcFileRef = NULL; if (linesType == 0) { CodeViewInfo::InlineeSourceLine& lineInfo = GET(CodeViewInfo::InlineeSourceLine); srcFileIdx = checksumFileRefs[lineInfo.fileId]; startSrcFileRef = &compileUnit->mSrcFileRefs[srcFileIdx]; startLine = lineInfo.sourceLineNum; inlinee = lineInfo.inlinee; } else if (linesType == 1) { CodeViewInfo::InlineeSourceLineEx& lineInfo = *(CodeViewInfo::InlineeSourceLineEx*)data; srcFileIdx = checksumFileRefs[lineInfo.fileId]; startSrcFileRef = &compileUnit->mSrcFileRefs[srcFileIdx]; startLine = lineInfo.sourceLineNum; inlinee = lineInfo.inlinee; data += sizeof(CodeViewInfo::InlineeSourceLineEx); data += (int)sizeof(CV_off32_t) * lineInfo.countOfExtraFiles; } else BF_FATAL("Invalid DEBUG_S_INLINEELINES lines type"); CvInlineInfo* inlineData = NULL; inlineDataDict.TryGetValue(inlinee, &inlineData); while (inlineData != NULL) { bool inlineDebugDump = inlineData->mDebugDump; if (inlineData->mInlinee == -1) { if (inlineDebugDump) BfLogCv("Duplicate data: %X\n", inlinee); break; } // Mark as done inlineData->mInlinee = -1; int chunkNum = 0; int curLine = startLine; int curValidLine = curLine; bool flushOnLineOffset = false; addr_target lastLineAddr = 0; DbgSrcFileReference* srcFileRef = startSrcFileRef; DbgSubprogram* curSubprogram = inlineData->mSubprogram; DbgSubprogram* inlineParent = inlineData->mSubprogram->GetRootInlineParent(); addr_target curAddr = inlineParent->mBlock.mLowPC; lastLineAddr = 0; if (inlineDebugDump) { //inlinerData->mWantSummaryDump = true; BfLogCv("------------------------------------------------\nINLINE DATA:%X Idx:%d CurAddr:%@ Line:%d\n SubProgram:%@ %s\n File:%s\n", inlinee, inlineIdx, curAddr, curLine, curSubprogram, curSubprogram->mName, srcFileRef->mSrcFile->mFilePath.c_str()); } uint8* annData = inlineData->mData; uint8* annDataEnd = inlineData->mData + inlineData->mDataLen; DbgLineData* curLineData = NULL; auto _AddLine = [&]() { if (chunkNum != 0) { curLineData = NULL; return; } DbgLineData lineData; lineData.mLine = curValidLine - 1; lineData.mRelAddress = (uint32)(curAddr - mImageBase); lineData.mColumn = 0; if (curLine <= 0) // Negative lines mean "invalid position" for inlining lineData.mColumn = -1; lineData.mContribSize = 0; if ((curSubprogram->mBlock.mLowPC == 0) && (curLineData != NULL)) curSubprogram->mBlock.mLowPC = mImageBase + curLineData->mRelAddress; if (inlineDebugDump) BfLogCv(" Adding Line:%d Addr:%@\n", lineData.mLine + 1, lineData.mRelAddress + mImageBase); curLineData = lineBuilder.Add(compileUnit, lineData, srcFileRef->mSrcFile, curSubprogram); }; int codeIdx = 0; while (annData < annDataEnd) { auto annVal = CodeViewInfo::CVUncompressData(annData); switch (annVal) { case CodeViewInfo::BA_OP_Invalid: // link time pdb contains PADDINGs break; case CodeViewInfo::BA_OP_CodeOffset: // param : start offset { int32 offset = (int32)CodeViewInfo::CVUncompressData(annData); curAddr = inlineParent->mBlock.mLowPC + offset; if (inlineDebugDump) BfLogCv(" BA_OP_CodeOffset(%d) CurAddr:%@ Line:%d\n", offset, curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeCodeOffsetBase: // param : nth separated code chunk (main code chunk == 0) { chunkNum = (int32)CodeViewInfo::CVUncompressData(annData); if (inlineDebugDump) BfLogCv("xxxxxxxxx BA_OP_ChangeCodeOffsetBase(%d) CurAddr:%@ Line:%d\n", chunkNum, curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeCodeOffset: // param : delta of offset { int32 offset = (int32)CodeViewInfo::CVUncompressData(annData); curAddr += offset; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeCodeOffset(%d) CurAddr:%@ Line:%d\n", offset, curAddr, curLine); if ((curLineData != NULL) && (curLineData->mContribSize == 0)) { curLineData->mRelAddress = (uint32)(curAddr - mImageBase); if (inlineDebugDump) BfLogCv(" Setting addr on previous entry\n"); } else _AddLine(); flushOnLineOffset = true; } break; case CodeViewInfo::BA_OP_ChangeCodeLength: // param : length of code, default next start { int newCodeLen = (int32)CodeViewInfo::CVUncompressData(annData); curAddr += newCodeLen; if (curLineData != NULL) curLineData->mContribSize = newCodeLen; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeCodeLength(%d) Addr:%@ EndAddr:%@\n", newCodeLen, curLineData->mRelAddress + mImageBase, curLineData->mRelAddress + mImageBase + curLineData->mContribSize); } break; case CodeViewInfo::BA_OP_ChangeFile: // param : fileId { uint32 newFileId = CodeViewInfo::CVUncompressData(annData); srcFileRef = &compileUnit->mSrcFileRefs[checksumFileRefs[newFileId]]; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeFile(%s) CurAddr:%@ Line:%d\n", srcFileRef->mSrcFile->mFilePath.c_str(), curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeLineOffset: // param : line offset (signed) { int32 lineOfs = (int32)CodeViewInfo::DecodeSignedInt32(CodeViewInfo::CVUncompressData(annData)); curLine += lineOfs; if (curLine > 0) curValidLine = curLine; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeLineOffset(%d) CurAddr:%@ Line:%d\n", lineOfs, curAddr, curLine); _AddLine(); } break; case CodeViewInfo::BA_OP_ChangeLineEndDelta: // param : how many lines, default 1 { int numLines = (int32)CodeViewInfo::CVUncompressData(annData); if (inlineDebugDump) BfLogCv(" BA_OP_ChangeLineEndDelta(%d) CurAddr:%@ Line:%d\n", numLines, curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeRangeKind: // param : either 1 (default, for statement) or 0 (for expression) { int32 readKind = (int32)CodeViewInfo::CVUncompressData(annData); if (inlineDebugDump) BfLogCv(" BA_OP_ChangeRangeKind(%d) CurAddr:%@ Line:%d\n", readKind, curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeColumnStart: // param : start column number, 0 means no column info { int column = (int32)CodeViewInfo::CVUncompressData(annData); if (curLineData != NULL) curLineData->mColumn = column; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeColumnStart(%d) CurAddr:%@ Line:%d\n", column, curAddr, curLine); } break; case CodeViewInfo::BA_OP_ChangeColumnEndDelta: // param : end column number delta (signed) { int columnEndDelta = (int32)CodeViewInfo::DecodeSignedInt32(CodeViewInfo::CVUncompressData(annData)); if (inlineDebugDump) BfLogCv(" BA_OP_ChangeColumnEndDelta(%d) CurAddr:%@ Line:%d\n", columnEndDelta, curAddr, curLine); } break; // Combo opcodes for smaller encoding size. case CodeViewInfo::BA_OP_ChangeCodeOffsetAndLineOffset: // param : ((sourceDelta << 4) | CodeDelta) { int offsetData = (int32)CodeViewInfo::CVUncompressData(annData); int codeDelta = offsetData & 0xF; int sourceDelta = CodeViewInfo::DecodeSignedInt32(offsetData >> 4); curAddr += codeDelta; curLine += sourceDelta; if (curLine > 0) curValidLine = curLine; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeCodeOffsetAndLineOffset(%d, %d) CurAddr:%@ Line:%d\n", codeDelta, sourceDelta, curAddr, curLine); _AddLine(); } break; case CodeViewInfo::BA_OP_ChangeCodeLengthAndCodeOffset: // param : codeLength, codeOffset { curValidLine = curLine; int codeLen = (int32)CodeViewInfo::CVUncompressData(annData); int codeOffset = (int32)CodeViewInfo::CVUncompressData(annData); curAddr += codeOffset; if (inlineDebugDump) BfLogCv(" BA_OP_ChangeCodeLengthAndCodeOffset(%d, %d) CurAddr:%@ Line:%d\n", codeLen, codeOffset, curAddr, curLine); _AddLine(); if (curLineData != NULL) { if (inlineDebugDump) BfLogCv(" ContribSize:%d\n", codeLen); curLineData->mContribSize = codeLen; } } break; case CodeViewInfo::BA_OP_ChangeColumnEnd: // param : end column number { int columnEnd = (int32)CodeViewInfo::CVUncompressData(annData); if (inlineDebugDump) BfLogCv(" BA_OP_ChangeColumnEnd(%d) CurAddr:%@ Line:%d\n", columnEnd, curAddr, curLine); } break; } codeIdx++; } if (curLineData != NULL) { if (curSubprogram->mBlock.mLowPC == 0) curSubprogram->mBlock.mLowPC = mImageBase + curLineData->mRelAddress; curSubprogram->mBlock.mHighPC = mImageBase + curLineData->mRelAddress + curLineData->mContribSize; } inlineData = inlineData->mNext; } } } data = dataEnd; } // for (auto& deferredInlinerDataKV : deferredInlinerDatas) // { // auto inlinerParent = deferredInlinerDataKV.mKey; // auto deferredInlinerData = &deferredInlinerDataKV.mValue; // // inlinerParent->mInlinerData = mAlloc.Alloc(); // inlinerParent->mInlinerData->mInlinees.CopyFrom(deferredInlinerData->mInlinees.mVals, deferredInlinerData->mInlinees.mSize, mAlloc); // inlinerParent->mInlinerData->mInlineLineData.CopyFrom(deferredInlinerData->mInlineLineData.mVals, deferredInlinerData->mInlineLineData.mSize, mAlloc); // //deferredInlinerData-> // // if (deferredInlinerData->mWantSummaryDump) // { // BfLogCv("------------------------------\nSUMMARY FOR: %s\n", inlinerParent->mName); // // for (auto& inlineLine : inlinerParent->mInlinerData->mInlineLineData) // { // BfLogCv("Addr:%@ EndAddr:%@ Line:%d Column:%d Inlinee:%d\n", inlineLine.mAddress, inlineLine.mAddress + inlineLine.mContribSize, // inlineLine.mLine + 1, inlineLine.mColumn + 1, inlineLine.mInlineIdx); // } // } // } } lineBuilder.Commit(); //OutputDebugStrF("Module loaded, AllocSize added: %d\n", (mAlloc.GetAllocSize() - allocSizeStart) / 1024); return compileUnit; } CvCompileUnit* COFF::ParseCompileUnit(int compileUnitId) { CvModuleInfo* moduleInfo = mCvModuleInfo[compileUnitId]; if (moduleInfo->mCompileUnit != NULL) return moduleInfo->mCompileUnit; BP_ZONE("ParseCompileUnit"); ParseTypeData(); if (moduleInfo->mStream == -1) return NULL; int sectionSize = 0; uint8* sectionData = CvReadStream(moduleInfo->mStream, §ionSize); ParseCompileUnit(moduleInfo, NULL, sectionData, sectionSize); delete sectionData; return moduleInfo->mCompileUnit; } DbgType* COFF::CvGetTypeOrNamespace(char* name, DbgLanguage language) { if (language == DbgLanguage_Unknown) { if (strncmp(name, "_bf::", 5) == 0) { language = DbgLanguage_Beef; name += 5; } else if ((name[0] == 'G') && (name[1] == '$')) { language = DbgLanguage_Beef; } else { language = DbgLanguage_C; // Check for a primitive array type like 'double[]' for (int i = 0; i < 10; i++) { char c = name[i]; if (c == 0) break; if (c == '[') language = DbgLanguage_Beef; } } } auto linkedModule = GetLinkedModule(); auto dbgTypeEntry = linkedModule->mTypeMap.Find(name, language); if (dbgTypeEntry != NULL) { auto dbgType = dbgTypeEntry->mValue; if (dbgType->mHotNewType != NULL) return dbgType->mHotNewType; return dbgType; } // It's possible that we get a reference to a template name that isn't actually in our type database // These may indicate VS compiler bugs or anonymous namespaces bool isValidName = true; for (const char* cPtr = name; *cPtr != '\0'; cPtr++) if ((*cPtr == '<') || (*cPtr == '`') || (*cPtr == '$')) isValidName = false; if (!isValidName) return NULL; //OutputDebugStrF("CvGetTypeOrNamespace Creating: %s\n", name); char* lastDblColon = (char*)GetLastDoubleColon(name); DbgType* dbgType = CvCreateType(); DbgType* parentType = mMasterCompileUnit->mGlobalType; if (lastDblColon != NULL) { *lastDblColon = 0; parentType = CvGetTypeOrNamespace(name, language); dbgType->mTypeName = DbgDupString(lastDblColon + 2, "DbgDupString.TypeOrNamespace0"); *lastDblColon = ':'; dbgType->mName = DbgDupString(name, "DbgDupString.TypeOrNamespace1"); } else { dbgType->mTypeName = DbgDupString(name, "DbgDupString.TypeOrNamespace2"); dbgType->mName = dbgType->mTypeName; } parentType->mSubTypeList.PushBack(dbgType); dbgType->mTypeCode = DbgType_Namespace; dbgType->mLanguage = language; linkedModule->mTypeMap.Insert(dbgType); return dbgType; } void COFF::MapCompileUnitMethods(DbgCompileUnit* compileUnit) { bool addHotTypes = (mIsHotObjectFile) && (mHotPrimaryTypes.size() == 0); DbgSubprogram* prevDbgMethod = NULL; DbgSubprogram* dbgMethod = compileUnit->mOrphanMethods.mHead; while (dbgMethod != NULL) { auto nextDbgMethod = dbgMethod->mNext; bool movedMethod = false; if (dbgMethod->mHasQualifiedName) { char* name = (char*)dbgMethod->mName; char* lastDblColon = (char*)GetLastDoubleColon(name); if (lastDblColon != NULL) { dbgMethod->mName = lastDblColon + 2; dbgMethod->mHasQualifiedName = false; const char* typeName = name; *lastDblColon = 0; DbgType* parentType = CvGetTypeOrNamespace(name); if (parentType != NULL) { if (addHotTypes) { mHotPrimaryTypes.Add(parentType); } compileUnit->mOrphanMethods.Remove(dbgMethod, prevDbgMethod); parentType->mMethodList.PushBack(dbgMethod); dbgMethod->mParentType = parentType; movedMethod = true; } } } if (!movedMethod) prevDbgMethod = dbgMethod; dbgMethod = nextDbgMethod; } } void COFF::MapCompileUnitMethods(int compileUnitId) { CvModuleInfo* moduleInfo = mCvModuleInfo[compileUnitId]; if (moduleInfo->mHasMappedMethods) return; ParseCompileUnit(compileUnitId); auto compileUnit = moduleInfo->mCompileUnit; //for (auto dbgMethod : compileUnit->mGlobalType.mMethodList) MapCompileUnitMethods(compileUnit); moduleInfo->mHasMappedMethods = true; } void COFF::PopulateType(DbgType* dbgType) { uint8* data = CvGetTagData(dbgType->mTagIdx, false); CvAutoReleaseTempData releaseTempData(this, data); uint8* dataStart = data; uint16 trLeafType = GET(uint16); switch (trLeafType) { case LF_ENUM: { lfEnum& enumInfo = *(lfEnum*)dataStart; CvParseMembers(dbgType, enumInfo.field, false); } break; case LF_STRUCTURE: case LF_CLASS: { lfClass& classInfo = *(lfClass*)dataStart; CvParseMembers(dbgType, classInfo.field, false); } break; case LF_UNION: { lfUnion& classInfo = *(lfUnion*)dataStart; CvParseMembers(dbgType, classInfo.field, false); } break; default: BF_FATAL("Invalid type"); } } void COFF::PopulateTypeGlobals(DbgType* dbgType) { //gDbgPerfManager->StartRecording(); { BP_ZONE_F("COFF::PopulateTypeGlobals %s", (dbgType->mName != NULL) ? dbgType->mName : "?"); ParseTypeData(); dbgType->mNeedsGlobalsPopulated = false; dbgType->mHasGlobalsPopulated = true; mGlobalsTargetType = dbgType; ParseSymbolStream(CvSymStreamType_Globals_Targeted); mGlobalsTargetType = NULL; } //gDbgPerfManager->StopRecording(true); } void COFF::PopulateSubprogram(DbgSubprogram* dbgSubprogram) { if (dbgSubprogram->mDeferredInternalsSize == 0) return; BP_ZONE("COFF::PopulateSubprogram"); auto compileUnit = (CvCompileUnit*)dbgSubprogram->mCompileUnit; CvModuleInfo* moduleInfo = mCvModuleInfo[compileUnit->mModuleIdx]; int sectionSize = 0; uint8* sectionData = CvReadStreamSegment(moduleInfo->mStream, dbgSubprogram->mTagIdx, dbgSubprogram->mDeferredInternalsSize); CvInlineInfoVec inlineDataVec; ParseCompileUnit_Symbols(compileUnit, NULL, sectionData, sectionData + dbgSubprogram->mDeferredInternalsSize, inlineDataVec, false, dbgSubprogram); delete sectionData; dbgSubprogram->mDeferredInternalsSize = 0; } void COFF::FixupInlinee(DbgSubprogram* dbgSubprogram, uint32 ipiTag) { if (!mCvIPIReader.IsSetup()) CvParseIPI(); //int idx = (ipiTag & 0x7FFFFFFF) - mCvIPIMinTag; //uint8* dataStart = mCvIPIData + mCvIPITagStartMap[idx]; uint8* dataStart = CvGetTagData(ipiTag & 0x7FFFFFFF, true); CvAutoReleaseTempData releaseTempData(this, dataStart); lfEasy& funcLeaf = *(lfEasy*)dataStart; switch (funcLeaf.leaf) { case LF_FUNC_ID: { lfFuncId* funcData = (lfFuncId*)dataStart; CvParseMethod(NULL, NULL, funcData->type, false, dbgSubprogram); dbgSubprogram->mName = (const char*)funcData->name; bool doScope = true; if ((funcData->scopeId != 0) && (doScope)) { uint8* data = CvGetTagData(funcData->scopeId, true); CvAutoReleaseTempData releaseTempData(this, data); lfEasy& leaf = *(lfEasy*)data; switch (leaf.leaf) { case LF_STRING_ID: { lfStringId& str = *(lfStringId*)data; const char* parentStr = (const char*)str.name; int allocLen = strlen(parentStr) + 2 + strlen(dbgSubprogram->mName) + 1; BP_ALLOC("String", allocLen); char* dupStr = (char*)mAlloc.AllocBytes(allocLen, "String"); strcpy(dupStr, parentStr); strcat(dupStr, "::"); strcat(dupStr, dbgSubprogram->mName); dbgSubprogram->mName = dupStr; } break; case LF_UDT_MOD_SRC_LINE: { /*auto udtModSrcLine = *(lfUdtModSrcLine*)data; auto parentType = CvGetType(udtModSrcLine.type); curSubprogram->mParentType = parentType;*/ } break; } } break; } case LF_MFUNC_ID: { lfMFuncId* funcData = (lfMFuncId*)dataStart; CvParseMethod(NULL, NULL, funcData->type, false, dbgSubprogram); dbgSubprogram->mName = (const char*)funcData->name; dbgSubprogram->mParentType = CvGetType(funcData->parentType); } break; } } int COFF::MapImport(CvCompileUnit* compileUnit, int id) { BF_ASSERT((id & 0x80000000) != 0); int scopeIdx = (id & 0x7FFF0000) >> 20; int itemId = (id & 0x0000FFFF); if (mModuleNameSet.IsEmpty()) { for (auto moduleInfo : mCvModuleInfo) { String name = moduleInfo->mModuleName; name = ToUpper(name); CvModuleInfoNameEntry entry; entry.mModuleInfo = moduleInfo; mModuleNameSet.Add(entry); } } if (scopeIdx < (int)compileUnit->mImports.size()) { auto& importSection = compileUnit->mImports[scopeIdx]; if (itemId < (int)importSection.mImports.size()) { auto importId = importSection.mImports[itemId]; if (importSection.mScopeCompileUnit == NULL) { CvModuleInfo* moduleInfo = NULL; size_t nameHash = CvModuleInfoNameEntry::GetHashCode(importSection.mScopeName); for (auto& nameEntry : mModuleNameSet.SelectHashes(nameHash)) { String checkModuleName = nameEntry.mModuleInfo->mModuleName; checkModuleName.Replace("\\", "/"); checkModuleName = ToUpper(checkModuleName); String checkScopeName = importSection.mScopeName; checkScopeName.Replace("\\", "/"); checkScopeName = ToUpper(checkScopeName); bool matches = false; if (checkModuleName == checkScopeName) matches = true; else if (checkScopeName.EndsWith("/" + checkModuleName)) matches = true; if (matches) { ParseCompileUnit(nameEntry.mModuleInfo->mIdx); auto externCompileUnit = nameEntry.mModuleInfo->mCompileUnit; if (externCompileUnit->mExportMap.IsEmpty()) { for (auto& exportEntry : externCompileUnit->mExports) externCompileUnit->mExportMap[exportEntry.mLocalId] = exportEntry.mGlobalId; } if (externCompileUnit->mExportMap.ContainsKey(importId)) { importSection.mScopeCompileUnit = nameEntry.mModuleInfo->mCompileUnit; break; } } } } if (importSection.mScopeCompileUnit != NULL) { uint32 exportId; if (importSection.mScopeCompileUnit->mExportMap.TryGetValue(importId, &exportId)) { if ((exportId & 0x80000000) != 0) { // Does this mean a recursive lookup? return exportId & 0x7FFFFFFF; } else { return exportId; } } } } } return 0; } void COFF::FixupInlinee(DbgSubprogram* dbgSubprogram) { BF_ASSERT(dbgSubprogram->mInlineeInfo != NULL); BF_ASSERT((dbgSubprogram->mInlineeInfo->mInlineeId & 0x80000000) != 0); int scopeIdx = (dbgSubprogram->mInlineeInfo->mInlineeId & 0x7FFF0000) >> 20; int itemId = (dbgSubprogram->mInlineeInfo->mInlineeId & 0x0000FFFF); if (mModuleNameMap.IsEmpty()) { for (auto moduleInfo : mCvModuleInfo) { mModuleNameMap[CaseInsensitiveString(moduleInfo->mModuleName)] = moduleInfo; } } auto compileUnit = (CvCompileUnit*)dbgSubprogram->mCompileUnit; if (scopeIdx < (int)compileUnit->mImports.size()) { auto& importSection = compileUnit->mImports[scopeIdx]; if (itemId < (int)importSection.mImports.size()) { auto importId = importSection.mImports[itemId]; if (importSection.mScopeCompileUnit == NULL) { CvModuleInfo* moduleInfo = NULL; mModuleNameMap.TryGetValue(CaseInsensitiveString(importSection.mScopeName), &moduleInfo); if (moduleInfo != NULL) { ParseCompileUnit(moduleInfo->mIdx); importSection.mScopeCompileUnit = moduleInfo->mCompileUnit; } if (importSection.mScopeCompileUnit != NULL) { for (auto& exportEntry : importSection.mScopeCompileUnit->mExports) { if (exportEntry.mLocalId == importId) { if (exportEntry.mGlobalId < mCvIPITagStartMap.size()) { FixupInlinee(dbgSubprogram, exportEntry.mGlobalId); } } } } } } } dbgSubprogram->mInlineeInfo->mInlineeId = 0; } void COFF::PopulateStaticVariableMap() { if (mPopulatedStaticVariables) return; BP_ZONE("COFF::PopulateStaticVariableMap"); if (mMasterCompileUnit != NULL) { PopulateTypeGlobals(mMasterCompileUnit->mGlobalType); for (auto variable : mMasterCompileUnit->mGlobalType->mMemberList) { if ((variable->mIsExtern) && (variable->mLinkName != NULL)) mStaticVariables.push_back(variable); } } DbgModule::PopulateStaticVariableMap(); } void COFF::ScanCompileUnit(int compileUnitId) { BP_ZONE("COFF::ScanCompileUnit"); CvModuleInfo* moduleInfo = mCvModuleInfo[compileUnitId]; if (moduleInfo->mStream < 0) return; BF_ASSERT(moduleInfo->mSymbolBytes % 4 == 0); uint8* sectionData = CvReadStreamSegment(moduleInfo->mStream, moduleInfo->mSymbolBytes, moduleInfo->mLinesBytes); uint8* data = sectionData; uint8* dataEnd = sectionData + moduleInfo->mLinesBytes; while (data < dataEnd) { GET_INTO(int32, lineInfoType); GET_INTO(int32, lineInfoLength); uint8* dataEnd = data + lineInfoLength; if (lineInfoType == DEBUG_S_FILECHKSMS) { while (data < dataEnd) { GET_INTO(uint, fileTableOfs); DbgSrcFile* srcFile; DbgSrcFile** srcFilePtr = NULL; if (!mCVSrcFileRefCache.TryAdd(fileTableOfs, NULL, &srcFilePtr)) { srcFile = *srcFilePtr; } else { const char* fileName = mStringTable.mStrTable + fileTableOfs; srcFile = AddSrcFile(NULL, fileName); mSrcFileDeferredRefs.Add(srcFile); *srcFilePtr = srcFile; //mCVSrcFileRefCache[fileTableOfs] = srcFile; } DbgDeferredSrcFileReference dbgDeferredSrcFileReference; dbgDeferredSrcFileReference.mDbgModule = this; dbgDeferredSrcFileReference.mCompileUnitId = compileUnitId; srcFile->mDeferredRefs.push_back(dbgDeferredSrcFileReference); srcFile->mHadLineData = true; GET_INTO(uint8, hashLen); GET_INTO(uint8, hashType); data += hashLen; PTR_ALIGN(data, sectionData, 4); } break; // Stop once we handle the file checksums } data = dataEnd; } delete [] sectionData; } void COFF::FixTypes(int startingIdx) { BP_ZONE("COFF::FixTypes"); //std::unordered_map namespaceMap; auto linkedModule = GetLinkedModule(); String wholeNamespace; for (int typeIdx = startingIdx; typeIdx < (int)linkedModule->mTypes.size(); typeIdx++) { DbgType* dbgType = linkedModule->mTypes[typeIdx]; DbgType* prevNamespaceType = NULL; if (dbgType->mName == NULL) { // Internal typedef continue; } if (dbgType->mLanguage == DbgLanguage_Unknown) { if (strncmp(dbgType->mName, "_bf::", 5) == 0) { dbgType->mLanguage = DbgLanguage_Beef; dbgType->mName += 5; dbgType->mTypeName += 5; } else if ((dbgType->mName[0] == 'G') && (dbgType->mName[1] == '$')) { dbgType->mLanguage = DbgLanguage_Beef; } else { dbgType->mLanguage = DbgLanguage_C; // Check for a primitive array type like 'double[]' for (int i = 0; i < 10; i++) { char c = dbgType->mName[i]; if (c == 0) break; if (c == '[') dbgType->mLanguage = DbgLanguage_Beef; } } } if (dbgType->mIsDeclaration) continue; if (mIsHotObjectFile) { auto entry = linkedModule->mTypeMap.Find(dbgType->mName, dbgType->mLanguage); if (entry != NULL) { // Just do hot replacing unless this type is new continue; } } // This never happens anymore - nested types are always handled as "internal typedefs" //TODO: Check the 'isnested' flag on the leaf instead of counting on parent to be specified? bool hadParent = dbgType->mParent != NULL; int chevronDepth = 0; if ((dbgType->mTypeCode == DbgType_Class) || (dbgType->mTypeCode == DbgType_Struct) || (dbgType->mTypeCode == DbgType_Union) || (dbgType->mTypeCode == DbgType_Enum) || (dbgType->mTypeCode == DbgType_TypeDef)) { int startIdx = -1; const char* name = dbgType->mTypeName; for (int i = 0; true; i++) { char c = name[i]; if (c == '<') chevronDepth++; else if (c == '>') chevronDepth--; if ((chevronDepth == 0) && ((c == ':') || (c == '.'))) { if ((!hadParent) && (i - startIdx > 1)) { //String wholeNamespace = String(name, name + i); wholeNamespace.clear(); wholeNamespace.Insert(0, name, i); auto entry = linkedModule->mTypeMap.Find(wholeNamespace.c_str(), dbgType->mLanguage); if (entry == NULL) { DbgType* namespaceType = CvCreateType(); namespaceType->mTypeCode = DbgType_Namespace; namespaceType->mName = DbgDupString(wholeNamespace.c_str(), "DbgDupString.FixTypes"); namespaceType->mTypeName = namespaceType->mName + startIdx + 1; namespaceType->mPriority = DbgTypePriority_Primary_Implicit; namespaceType->mLanguage = dbgType->mLanguage; linkedModule->mTypeMap.Insert(namespaceType); if (prevNamespaceType != NULL) { namespaceType->mParent = prevNamespaceType; prevNamespaceType->mSubTypeList.PushBack(namespaceType); } prevNamespaceType = namespaceType; } else { prevNamespaceType = entry->mValue; } } startIdx = i; } else if ((c == 0) /*|| (c == '<')*/ || (c == '(')) break; } if (prevNamespaceType != NULL) { dbgType->mParent = prevNamespaceType; prevNamespaceType->mSubTypeList.PushBack(dbgType); } dbgType->mTypeName = dbgType->mTypeName + startIdx + 1; // Just take last part of name } if (dbgType->mParent == NULL) dbgType->mCompileUnit->mGlobalType->mSubTypeList.PushBack(dbgType); } } void COFF::CvReadStream(int streamIdx, CvStreamReader& streamReader) { BF_ASSERT(streamReader.mCOFF == NULL); streamReader.mCOFF = this; streamReader.mPageBits = mCvPageBits; int streamSize = mCvStreamSizes[streamIdx]; int streamPageCount = (streamSize + mCvPageSize - 1) / mCvPageSize; streamReader.mStreamPtrs.Resize(streamPageCount); streamReader.mSize = streamSize; int streamPtrIdx = mCvStreamPtrStartIdxs[streamIdx]; for (int streamPageIdx = 0; streamPageIdx < streamPageCount; streamPageIdx++) { streamReader.mStreamPtrs[streamPageIdx] = (uint8*)mCvMappedViewOfFile + mCvStreamPtrs[streamPtrIdx] * mCvPageSize; streamPtrIdx++; } } void COFF::CvInitStreamRaw(CvStreamReader & streamReader, uint8 * data, int size) { BF_ASSERT(streamReader.mCOFF == NULL); streamReader.mCOFF = this; streamReader.mPageBits = 31; streamReader.mStreamPtrs.Add(data); streamReader.mSize = size; } uint8* COFF::CvReadStream(int streamIdx, int* outSize) { BP_ZONE("ReadCvSection"); if (streamIdx >= mCvStreamSizes.size()) return NULL; int streamSize = mCvStreamSizes[streamIdx]; if (outSize != NULL) *outSize = streamSize; if (streamSize <= 0) return NULL; int streamPageCount = (streamSize + mCvPageSize - 1) / mCvPageSize; uint8* sectionData = new uint8[streamSize]; bool deferDeleteSectionData = false; int streamPtrIdx = mCvStreamPtrStartIdxs[streamIdx]; for (int streamPageIdx = 0; streamPageIdx < streamPageCount; streamPageIdx++) { mCvDataStream->SetPos(mCvStreamPtrs[streamPtrIdx] * mCvPageSize); mCvDataStream->Read(sectionData + streamPageIdx * mCvPageSize, std::min(streamSize - (streamPageIdx * mCvPageSize), mCvPageSize)); streamPtrIdx++; } return sectionData; } uint8* COFF::CvReadStreamSegment(int streamIdx, int offset, int size) { BP_ZONE("ReadCvSection"); //int streamSize = mCvStreamSizes[streamIdx]; //int streamPageCount = (streamSize + mCvPageSize - 1) / mCvPageSize; uint8* sectionData = new uint8[size]; //delete [] sectionData; bool deferDeleteSectionData = false; uint8* data = sectionData; int curOffset = offset; int sizeLeft = size; int streamPtrIdx = mCvStreamPtrStartIdxs[streamIdx]; streamPtrIdx += offset / mCvPageSize; curOffset = offset - (offset / mCvPageSize) * mCvPageSize; //for (int streamPageIdx = 0; streamPageIdx < streamPageCount; streamPageIdx++) while (sizeLeft > 0) { mCvDataStream->SetPos(mCvStreamPtrs[streamPtrIdx] * mCvPageSize + curOffset); int readSize = std::min(mCvPageSize - curOffset, sizeLeft); mCvDataStream->Read(data, readSize); data += readSize; sizeLeft -= readSize; curOffset = 0; streamPtrIdx++; } return sectionData; } void COFF::ReleaseTempBuf(uint8* buf) { if ((mTempBufIdx > 0) && (mTempBufs.mVals[mTempBufIdx - 1].mVals == buf)) mTempBufIdx--; } bool COFF::CvParseHeader(uint8 wantGuid[16], int32 wantAge) { int streamSize = 0; uint8* data = CvReadStream(1, &streamSize); mCvHeaderData = data; int32 pdbVersion = GET(int32); int32 timestamp = GET(int32); int32 pdbAge = GET(int32); for (int i = 0; i < 16; i++) mPDBGuid[i] = GET(int8); if ((wantAge != -1) && (/*(pdbAge != wantAge) ||*/ (memcmp(mPDBGuid, wantGuid, 16) != 0))) { if (mDebugger != NULL) { String msg = "PDB GUID did not match requested version\n"; msg += StrFormat(" Module GUID: "); for (int i = 0; i < 16; i++) msg += StrFormat("%02X", (uint8)wantGuid[i]); msg += "\n"; msg += StrFormat(" PDB GUID : "); for (int i = 0; i < 16; i++) msg += StrFormat("%02X", (uint8)mPDBGuid[i]); Fail(msg); } return false; } if (mParseKind == ParseKind_Header) return true; int nameTableIdx = -1; GET_INTO(int32, strTabLen); const char* strTab = (const char*)data; data += strTabLen; GET_INTO(int32, numStrItems); GET_INTO(int32, strItemMax); GET_INTO(int32, usedLen); data += usedLen * sizeof(int32); GET_INTO(int32, deletedLen); data += deletedLen * sizeof(int32); for (int tableIdx = 0; tableIdx < numStrItems; tableIdx++) { GET_INTO(int32, strOfs); GET_INTO(int32, streamNum); const char* tableName = strTab + strOfs; if (strcmp(tableName, "/names") == 0) mStringTable.mStream = streamNum; if (strcmp(tableName, "srcsrv") == 0) mCvSrcSrvStream = streamNum; /*if (tableIdx == nameTableIdx) { nameStringTable.mStream = streamNum; nameStringTable.mStreamOffset = strOfs; }*/ } return true; } bool COFF::CvParseDBI(int wantAge) { BP_ZONE("CvParseDBI"); uint8* data = CvReadStream(3); uint8* sectionData = data; defer { delete sectionData; }; // Header GET_INTO(int32, signature); GET_INTO(uint32, version); GET_INTO(uint32, age); mDebugAge = (int32)age; if ((wantAge != -1) && (wantAge != age)) { if (mDebugger != NULL) { String msg = "PDB age did not match requested age\n"; msg += StrFormat(" Module Age: %d PDB Age: %d", wantAge, age); Fail(msg); } return false; } if (mParseKind == ParseKind_Header) return true; mCvGlobalSymbolInfoStream = GET(int16); // hash1_file, snGSSyms GET_INTO(uint16, pdb_dll_version); mCvPublicSymbolInfoStream = GET(int16); // hash2_file, snPSSyms GET_INTO(uint16, pdb_dll_build_major); mCvSymbolRecordStream = GET(int16); // gsym_file, snSymRecs GET_INTO(uint16, pdb_dll_build_minor); GET_INTO(uint32, gp_modi_size); //module_size GET_INTO(uint32, section_contribution_size); //offset_size GET_INTO(uint32, section_map_size); //hash_size GET_INTO(uint32, file_info_size); //srcmodule_size GET_INTO(uint32, ts_map_size); //pdb_import_size GET_INTO(uint32, mfc_index); //resvd0 GET_INTO(uint32, dbg_header_size); // stream_index_size GET_INTO(uint32, ec_info_size); //unknown_size GET_INTO(uint16, flags); //resvd3 GET_INTO(uint16, machine); GET_INTO(uint32, reserved); //resvd4 uint8* headerEnd = data; // Skip to debug header data += gp_modi_size; data += section_contribution_size; data += section_map_size; data += file_info_size; data += ts_map_size; data += ec_info_size; // Debug header GET_INTO(int16, fpo); GET_INTO(int16, exception); GET_INTO(int16, fixup); GET_INTO(int16, omap_to_src); GET_INTO(int16, omap_from_src); GET_INTO(int16, section_header); // 0x0a, all others are -1 (segments) GET_INTO(int16, token_rid_map); GET_INTO(int16, x_data); GET_INTO(int16, p_data); mCvNewFPOStream = GET(int16); GET_INTO(int16, section_header_origin); if (section_header != -1) ParseSectionHeader(section_header); // Module info data = headerEnd; uint8* dataEnd = data + gp_modi_size; while (data < dataEnd) { BP_ALLOC_T(CvModuleInfo); CvModuleInfo* moduleInfo = mAlloc.Alloc(); memcpy(moduleInfo, data, sizeof(CvModuleInfoBase)); data += sizeof(CvModuleInfoBase); moduleInfo->mModuleName = CvParseAndDupString(data);//DataGetString(data); moduleInfo->mObjectName = CvParseAndDupString(data);//DataGetString(data); moduleInfo->mIdx = (int)mCvModuleInfo.size(); //BfLogCv("Module %d: %s Obj: %s\n", mCvModuleInfo.size(), moduleInfo->mModuleName, moduleInfo->mObjectName); mCvModuleInfo.push_back(moduleInfo); /*if (moduleInfo->mStream != -1) { if (moduleInfo->mStream >= mDeferredModuleInfo.size()) mDeferredModuleInfo.resize(moduleInfo->mStream + 1); mDeferredModuleInfo[moduleInfo->mStream].push_back(moduleInfo); }*/ PTR_ALIGN(data, sectionData, 4); } // Section contrib { BP_ZONE("CvParseDBI_Contrib"); dataEnd = data + section_contribution_size; int32 sig = GET(int32); BF_ASSERT((sig == 0xF12EBA2D) || (sig == 0xf13151e4)); bool isV2 = sig == 0xf13151e4; if (isV2) { Fail("ERROR: FastLink PDBs are not supported. Consider adding full debug info (/DEBUG:FULL)."); mIsFastLink = true; } while (data < dataEnd) { CvSectionContrib& contrib = GET(CvSectionContrib); if (isV2) { uint32_t isectCoff = GET(uint32_t); } for (int contribOffset = 0; true; contribOffset += DBG_MAX_LOOKBACK) { int curSize = contrib.mSize - contribOffset; if (curSize <= 0) break; BP_ALLOC_T(DbgCompileUnitContrib); auto contribEntry = mAlloc.Alloc(); contribEntry->mAddress = GetSectionAddr(contrib.mSection, contrib.mOffset) + contribOffset; contribEntry->mLength = curSize; contribEntry->mDbgModule = this; contribEntry->mCompileUnitId = contrib.mModule; mDebugTarget->mContribMap.Insert(contribEntry); } } } // Dbi section map dataEnd = data + section_map_size; GET_INTO(int16, numSections); GET_INTO(int16, numSectionsCopy); while (data < dataEnd) { GET_INTO(uint8, flags); GET_INTO(uint8, section_type); // This field hasn't been deciphered but it is always 0x00000000 or 0xFFFFFFFF // and modifying it doesn't seem to invalidate the Cv. GET_INTO(int32, unknownData); GET_INTO(uint16, section_number); // Same thing as for unknown_data_1. GET_INTO(int32, unknownData2); // Value added to the address offset when calculating the RVA. GET_INTO(uint32, rva_offset); GET_INTO(uint32, section_length); } // Dbi File info dataEnd = data + file_info_size; GET_INTO(uint16, fileInfoNumModules); GET_INTO(uint16, bad_fileInfoNumSourceFiles); int fileInfoNumSourceFiles = 0; uint16* modIndices = (uint16*)data; data += fileInfoNumModules * sizeof(uint16); uint16* modFileCounts = (uint16*)data; data += fileInfoNumModules * sizeof(uint16); for (int moduleIdx = 0; moduleIdx < fileInfoNumModules; moduleIdx++) fileInfoNumSourceFiles += modFileCounts[moduleIdx]; int32* fileNameOffsets = (int32*)data; data += fileInfoNumSourceFiles * sizeof(int32); char* nameBuffer = (char*)data; char* namePtr = nameBuffer; int curFileOfs = 0; for (int moduleIdx = 0; moduleIdx < fileInfoNumModules; moduleIdx++) { //TODO: LLD-LINK does not emit this properly //BF_ASSERT((modIndices[moduleIdx] == curFileOfs) || (moduleIdx >= 32)); for (int fileIdx = 0; fileIdx < modFileCounts[moduleIdx]; fileIdx++) { char* fileName = namePtr + *fileNameOffsets; curFileOfs++; fileNameOffsets++; } //int blockStart = fileBlockTable[i]; //int blockLength = fileBlockTable[fileInfoNumModules + i]; //int offset = offsetTable[blockStart]; through blockLength... } // File name table /*uint8* dataStart = data; while (data < dataEnd) { int offset = (int)(data - dataStart); const char* fileName = DataGetString(data); //DbgSrcFile* srcFile = AddSrcFile(mMasterCompileUnit, fileName); //fileTable.insert(std::make_pair(offset, srcFile)); }*/ data = dataEnd; BF_ASSERT(ts_map_size == 0); // EC Info dataEnd = data + ec_info_size; GET_INTO(uint32, strTabSig); GET_INTO(uint32, strTabVer); BF_ASSERT(strTabSig == 0xEFFEEFFE); GET_INTO(int32, strTabSize); uint8* strTabData = data; data += strTabSize; GET_INTO(int32, strTabEntryCount); for (int entryIdx = 0; entryIdx < strTabEntryCount; entryIdx++) { GET_INTO(int32, strOffset); const char* str = (const char*)strTabData + strOffset; } return true; } void COFF::ParseSectionHeader(int sectionIdx) { bool fakeRVAS = mSectionRVAs.empty(); int sectionSize = 0; uint8* sectionData = CvReadStream(sectionIdx, §ionSize); uint8* data = sectionData; uint8* dataEnd = data + sectionSize; while (data < dataEnd) { auto& sectionHeader = GET(PESectionHeader); if (fakeRVAS) { mSectionRVAs.push_back(sectionHeader.mVirtualAddress); } } if (fakeRVAS) mSectionRVAs.push_back(0); delete sectionData; } void COFF::CvParseIPI() { BF_ASSERT(!mCvIPIReader.IsSetup()); //uint8* sectionData; int sectionSize = 0; int hashAdjOffset = 0; int32 hashStream = -1; int32 hashAdjSize = 0; int32 dataOffset = 0; ParseTypeData(4, mCvIPIReader, sectionSize, dataOffset, hashStream, hashAdjOffset, hashAdjSize, mCvIPIMinTag, mCvIPIMaxTag); //mCvIPIData = sectionData; int recordCount = mCvIPIMaxTag - mCvIPIMinTag; mCvIPITagStartMap.Resize(recordCount); //uint8* data = sectionData + dataOffset; int offset = dataOffset; for (int idx = 0; idx < recordCount; idx++) { //BF_ASSERT(((offset) & 3) == 0); uint8* data = mCvIPIReader.GetTempPtr(offset, 4); uint16 trLength = GET(uint16); int offsetStart = offset; offset += 2; data = mCvIPIReader.GetTempPtr(offset, trLength); uint8* dataStart = data; uint16 trLeafType = GET(uint16); //uint8* dataEnd = dataStart + trLength; offset += trLength; /* */ lfEasy& funcLeaf = *(lfEasy*)dataStart; switch (funcLeaf.leaf) { case LF_FUNC_ID: { lfFuncId* funcData = (lfFuncId*)dataStart; //subprogram->mName = (const char*)funcData->name; bool doScope = true; /*if (strcmp(curSubprogram->mName, "DispatchToMethod") == 0) { doScope = true; }*/ if ((funcData->scopeId != 0) && (doScope)) { uint8* data = CvGetTagData(funcData->scopeId, true); CvAutoReleaseTempData releaseTempData(this, data); lfEasy& leaf = *(lfEasy*)data; switch (leaf.leaf) { case LF_STRING_ID: { lfStringId& str = *(lfStringId*)data; const char* parentStr = (const char*)str.name; } break; case LF_UDT_MOD_SRC_LINE: { /*auto udtModSrcLine = *(lfUdtModSrcLine*)data; auto parentType = CvGetType(udtModSrcLine.type); curSubprogram->mParentType = parentType;*/ } break; } } break; } case LF_MFUNC_ID: { lfMFuncId* funcData = (lfMFuncId*)dataStart; auto parentType = CvGetType(funcData->parentType); } break; } mCvIPITagStartMap[idx] = offsetStart; } } const char* COFF::CvParseSymbol(int offset, CvSymStreamType symStreamType, addr_target& outAddr) { const char* retName = NULL; int offsetStart = offset; uint8* data = mCvSymbolRecordReader.GetTempPtr(offset, 4); uint16 symLen = *(uint16*)data; bool madeCopy = false; data = mCvSymbolRecordReader.GetTempPtr(offset, symLen + 2, false, &madeCopy); uint8* dataStart = data; uint16 symType = *(uint16*)(data + 2); BF_ASSERT(symLen % 4 == 2); BF_ASSERT((intptr)data % 4 == 0); char* scanName = NULL; switch (symType) { case S_UDT: { UDTSYM& udt = *(UDTSYM*)dataStart; const char* name = (const char*)udt.name; #ifdef _DEBUG ParseTypeData(); auto type = CvGetType(udt.typind); #endif retName = name; } break; case S_PUB32: { PUBSYM32& pubSym = *(PUBSYM32*)dataStart; if (symStreamType != CvSymStreamType_Symbols) break; BP_ALLOC_T(DbgSymbol); DbgSymbol* dbgSymbol = mAlloc.Alloc(); const char* name = (const char*)mCvSymbolRecordReader.GetPermanentPtr(offsetStart + offsetof(PUBSYM32, name), symLen - offsetof(PUBSYM32, name) + 2); // pubSym.name; //OutputDebugStrF("Sym: %s\n", name); #ifdef BF_DBG_32 if ((name != NULL) && (name[0] == '_')) name++; #endif dbgSymbol->mName = name; dbgSymbol->mAddress = GetSectionAddr(pubSym.seg, pubSym.off); dbgSymbol->mDbgModule = this; if ((dbgSymbol->mAddress >= mImageBase) && (dbgSymbol->mAddress < mImageBase + mImageSize)) { if ((dbgSymbol->mAddress & 0xFFFF'0000'0000'0000) == 0) mDebugTarget->mSymbolMap.Insert(dbgSymbol); } else { NOP; } mSymbolNameMap.Insert(dbgSymbol); outAddr = dbgSymbol->mAddress; retName = name; } break; case S_CONSTANT: { CONSTSYM& constSym = *(CONSTSYM*)dataStart; } break; case S_GDATA32: case S_LDATA32: { uint8* permDataPtr = madeCopy ? mCvSymbolRecordReader.GetPermanentPtr(offsetStart, symLen + 2) : dataStart; DATASYM32& dataSym = *(DATASYM32*)permDataPtr; if (strcmp((const char*)dataSym.name, "_tls_index") == 0) { mTLSIndexAddr = GetSectionAddr(dataSym.seg, dataSym.off); } char* name = (char*)dataSym.name; retName = name; if (symStreamType == CvSymStreamType_Globals_Scan) { scanName = (char*)dataSym.name; break; } bool wasBeef = false; const char* memberName = CvCheckTargetMatch(name, wasBeef); if (memberName == NULL) break; DbgType* dbgType = CvGetType(dataSym.typind); BP_ALLOC_T(DbgVariable); DbgVariable* variable = mAlloc.Alloc(); variable->mType = dbgType; variable->mLocationData = permDataPtr; variable->mLocationLen = 1; variable->mIsStatic = true; variable->mCompileUnit = mMasterCompileUnit; variable->mName = memberName; variable->mLinkName = name; if (strcmp((const char*)dataSym.name, "__ImageBase") == 0) variable->mLinkName = NULL; // Avoid adding to map variable->mIsExtern = symType == S_GDATA32; variable->mCompileUnit = mGlobalsTargetType->mCompileUnit; // Push front so we will find before the original static declaration (that has no memory associated with it) mGlobalsTargetType->mMemberList.PushFront(variable); if (mIsHotObjectFile) { if ((variable->mIsExtern) && (variable->mLinkName != NULL)) mStaticVariables.push_back(variable); } } break; case S_GTHREAD32: case S_LTHREAD32: { uint8* permDataPtr = madeCopy ? mCvSymbolRecordReader.GetPermanentPtr(offsetStart, symLen + 2) : dataStart; THREADSYM32& threadSym = *(THREADSYM32*)permDataPtr; if (symStreamType == CvSymStreamType_Globals_Scan) { scanName = (char*)threadSym.name; break; } char* name = (char*)threadSym.name; retName = name; bool wasBeef = false; const char* memberName = CvCheckTargetMatch(name, wasBeef); if (memberName == NULL) break; addr_target addr = GetSectionAddr(threadSym.seg, threadSym.off); DbgType* dbgType = CvGetType(threadSym.typind); BP_ALLOC_T(DbgVariable); DbgVariable* variable = mAlloc.Alloc(); variable->mType = dbgType; variable->mLocationData = permDataPtr; variable->mLocationLen = 1; variable->mIsStatic = true; variable->mCompileUnit = mMasterCompileUnit; variable->mName = memberName; variable->mLinkName = name; variable->mIsExtern = symType == S_GTHREAD32; variable->mCompileUnit = mGlobalsTargetType->mCompileUnit; // Push front so we will find before the original static declaration (that has no memory associated with it) mGlobalsTargetType->mMemberList.PushFront(variable); } break; case S_PROCREF: { REFSYM2& refSym = *(REFSYM2*)dataStart; if (refSym.imod == 0) break; char* name = (char*)refSym.name; retName = name; if (symStreamType == CvSymStreamType_Globals_Scan) { scanName = (char*)refSym.name; break; } int moduleId = refSym.imod - 1; bool wasBeef = false; const char* memberName = CvCheckTargetMatch(name, wasBeef); if (memberName == NULL) break; bool isIllegal = false; for (const char* cPtr = memberName; *cPtr != 0; cPtr++) { char c = *cPtr; if ((c == '<') || (c == '`') || (c == '$')) isIllegal = true; } if (!isIllegal) { BP_ALLOC_T(DbgMethodNameEntry); auto methodNameEntry = mAlloc.Alloc(); methodNameEntry->mCompileUnitId = moduleId; methodNameEntry->mName = madeCopy ? DbgDupString(memberName) : memberName; mGlobalsTargetType->mMethodNameList.PushBack(methodNameEntry); } } break; default: //BF_FATAL("Unhandled"); break; } if (scanName != NULL) { //TODO: Remove //String origName = name; //CvFixupName(name); char* lastDblColon = (char*)GetNamespaceEnd(scanName); if (lastDblColon != NULL) { if (mPrevScanName != NULL) { int namespaceLen = lastDblColon - scanName; if (strncmp(scanName, mPrevScanName, namespaceLen + 2) == 0) // +2 includes the ending '::' { // We've already inserted this namespace return retName; } } StringT<256> tempName; tempName = String(scanName, lastDblColon - scanName); DbgType* dbgType = CvGetTypeOrNamespace((char*)tempName.c_str()); // *lastDblColon = '\0'; // DbgType* dbgType = CvGetTypeOrNamespace(scanName); // *lastDblColon = ':'; /*if (strcmp(lastDblColon, "::this") == 0) { dbgType->mLanguage = DbgLanguage_Beef; }*/ if (dbgType != NULL) dbgType->mNeedsGlobalsPopulated = true; mPrevScanName = scanName; /*const char* methodName = NULL; DbgType* dbgParent = NULL; if (lastDblColon != NULL) { *lastDblColon = 0; const char* namespaceName = name; dbgParent = CvGetTypeOrNamespace(namespaceName); methodName = lastDblColon + 2; } else { dbgParent = &mMasterCompileUnit->mGlobalType; //mStaticVariables.push_back(variable); methodName = name; } if (dbgParent != NULL) { auto methodNameEntry = mAlloc.Alloc(); methodNameEntry->mCompileUnitId = moduleId; methodNameEntry->mName = methodName; dbgParent->mMethodNameList.PushBack(methodNameEntry); }*/ } else { mMasterCompileUnit->mGlobalType->mNeedsGlobalsPopulated = true; } } return retName; } uint8* COFF::HandleSymStreamEntries(CvSymStreamType symStreamType, uint8* data, uint8* addrMap) { uint32 tickStart = BFTickCount(); GET_INTO(uint32, verSig); GET_INTO(uint32, verHdr); GET_INTO(int32, sizeHr); GET_INTO(int32, sizeBuckets); BF_ASSERT(verSig == 0xffffffff); BF_ASSERT(verHdr == 0xf12f091a); uint8* dataEnd = data + sizeHr; uint8* refDataHead = data; int entryIdxMax = sizeHr / 8; data = dataEnd; dataEnd = data + sizeBuckets ; int32* hashBucketHead = (int32*)(data + 0x81 * 4); int32* hashBucketPtr = hashBucketHead; int numRecordsRead = 0; #ifdef _DEBUG bool verify = false; #endif if (mIsFastLink) return dataEnd; // FOrmat changed std::multimap checkAddrMap; int bitCount = 0; for (int blockIdx = 0; blockIdx < 0x81; blockIdx++) { GET_INTO(uint32, bits); for (int i = 0; i < 32; i++) { if ((bits & (1 << i)) != 0) { bitCount++; int32 entryStart = *(hashBucketPtr++) / 12; int32 entryEnd; if ((uint8*)hashBucketPtr != (uint8*)dataEnd) entryEnd = *(hashBucketPtr) / 12; else entryEnd = entryIdxMax; BF_ASSERT(entryStart <= entryEnd); for (int entryIdx = entryStart; entryIdx < entryEnd; entryIdx++) { int32 symDataPtr = *(int32*)(refDataHead + entryIdx * 8); if (symDataPtr == 0) continue; addr_target addr = 0; const char* symName = CvParseSymbol(symDataPtr - 1, symStreamType, addr); #ifdef _DEBUG if ((verify) && (addrMap != NULL)) { checkAddrMap.insert(std::make_pair(addr, symDataPtr - 1)); } if ((verify) && (symName != NULL)) { int hashIdx = (blockIdx * 32) + i; int wantHash = BlHash::HashStr_PdbV1(symName) % 4096; BF_ASSERT(hashIdx == wantHash); } #endif numRecordsRead++; } } } } #ifdef _DEBUG if ((verify) && (addrMap != NULL)) { auto itr = checkAddrMap.begin(); data = addrMap; for (int entryIdx = 0; entryIdx < numRecordsRead; entryIdx++) { int verifyIdx = GET(int32); int actualIdx = itr->second; if (actualIdx != verifyIdx) { OutputDebugStrF("Mismatch #%X %X %X %08X\n", entryIdx, verifyIdx, actualIdx, itr->first); } ++itr; } } #endif BF_ASSERT(numRecordsRead == entryIdxMax); OutputDebugStrF("HandleSymStreamEntries Ticks: %d\n", BFTickCount() - tickStart); return dataEnd; } void COFF::ParseSymbolStream(CvSymStreamType symStreamType) { if (mCvSymbolRecordStream == 0) return; BP_ZONE("COFF::ParseSymbolStream"); if (!mCvSymbolRecordReader.IsSetup()) CvReadStream(mCvSymbolRecordStream, mCvSymbolRecordReader); if ((symStreamType == CvSymStreamType_Globals_Scan) || (symStreamType == CvSymStreamType_Globals_Targeted)) { int streamSize = 0; uint8* data; if (mCvGlobalSymbolData == NULL) mCvGlobalSymbolData = CvReadStream(mCvGlobalSymbolInfoStream, &streamSize); data = mCvGlobalSymbolData; HandleSymStreamEntries(symStreamType, data, NULL); } else { int streamSize = 0; uint8* data; if (mCvPublicSymbolData == NULL) mCvPublicSymbolData = CvReadStream(mCvPublicSymbolInfoStream, &streamSize); // psgsi data = mCvPublicSymbolData; uint8* sectionData = data; GET_INTO(int32, symHashSize); GET_INTO(int32, addrMapSize); GET_INTO(int32, thunkCount); GET_INTO(int32, thunkSize); GET_INTO(int32, thunkTableStream); GET_INTO(int32, thunkTableOfs); GET_INTO(int32, thunkSectCount); data = HandleSymStreamEntries(symStreamType, data, data + symHashSize); //TODO: Hm, not quite right? /*int addrMapEntryCount = addrMapSize / 4; for (int addrIdx = 0; addrIdx < addrMapEntryCount; addrIdx++) { GET_INTO(int32, addrVal); } for (int thunkIdx = 0; thunkIdx < thunkCount; thunkIdx++) { GET_INTO(int32, thunkVal); } for (int sectionIdx = 0; sectionIdx < thunkSectCount; sectionIdx++) { GET_INTO(int32, ofs); GET_INTO(int32, sectNum); }*/ } } void COFF::ParseGlobalsData() { if (!mPDBLoaded) return; if (mParsedGlobalsData) return; mParsedGlobalsData = true; //gDbgPerfManager->StartRecording(); int startTypeIdx = (int)mTypes.size(); ParseSymbolStream(CvSymStreamType_Globals_Scan); //gDbgPerfManager->StopRecording(true); //int addedTypes = (int)mTypes.size() - startTypeIdx; //OutputDebugStrF("Types Added: %d ProcSymCount: %d\n", addedTypes, mProcSymCount); } void COFF::ParseSymbolData() { if (!mPDBLoaded) return; if (mParsedSymbolData) return; mParsedSymbolData = true; ParseSymbolStream(CvSymStreamType_Symbols); } bool COFF::ParseCv(DataStream& pdbFS, uint8* rootDirData, int pageSize, uint8 wantGuid[16], int32 wantAge) { BP_ZONE("ParseCv"); mParsedGlobalsData = false; mParsedSymbolData = false; mParsedTypeData = false; mPopulatedStaticVariables = false; InitCvTypes(); int startingTypeIdx = mTypes.size(); uint8* data = rootDirData; bool failed = false; int numStreams = GET(int32); if (numStreams == 0) return true; mCvStreamSizes.Resize(numStreams); mCvStreamPtrStartIdxs.Resize(numStreams); int streamPages = 0; for (int i = 0; i < (int)mCvStreamSizes.size(); i++) mCvStreamSizes[i] = GET(int32); for (int streamIdx = 0; streamIdx < numStreams; streamIdx++) { mCvStreamPtrStartIdxs[streamIdx] = streamPages; if (mCvStreamSizes[streamIdx] > 0) streamPages += (mCvStreamSizes[streamIdx] + pageSize - 1) / pageSize; } mCvStreamPtrs.Resize(streamPages); for (int i = 0; i < (int)mCvStreamPtrs.size(); i++) mCvStreamPtrs[i] = GET(int32); ////////////////////////////////////////////////////////////////////////// if (!CvParseHeader(wantGuid, wantAge)) return false; if (!CvParseDBI(wantAge)) return false; if (mParseKind == ParseKind_Header) return true; //ParseSymbolData(); //TODO: Remove /*for (int streamIdx = 0; streamIdx < numStreams; streamIdx++) { int outSize; uint8* data = CvReadStream(streamIdx, &outSize); if (outSize > 0) { FILE* fp = fopen(StrFormat("c:/temp/test4/pdb_stream_%d.bin", streamIdx).c_str(), "wb"); fwrite(data, 1, outSize, fp); fclose(fp); delete [] data; } }*/ { BP_ZONE("COFF::ParseCv_ReadStrTable"); if (mStringTable.mStream != -1) { mCvStrTableData = CvReadStream(mStringTable.mStream); mStringTable.mStrTable = (const char*)mCvStrTableData + 12; } } for (int compileUnitId = 0; compileUnitId < (int)mCvModuleInfo.size(); compileUnitId++) { //ParseCompileUnit(compileUnitId); ScanCompileUnit(compileUnitId); } /*for (int streamIdx = 0; streamIdx < (int)mDeferredModuleInfo.size(); streamIdx++) { if (mDeferredModuleInfo[streamIdx].empty()) continue; uint8* data = CvReadStream(streamIdx); uint8* sectionData = data; for (auto& moduleInfo : mDeferredModuleInfo[streamIdx]) { CvParseModuleInfo(*moduleInfo, data, sectionData, mCvTypeSectionData, mStringTable); } }*/ ////////////////////////////////////////////////////////////////////////// return true; } void COFF::ReportMemory(MemReporter* memReporter) { DbgModule::ReportMemory(memReporter); memReporter->AddVec("mCvTypeMap", mCvTypeMap); memReporter->AddVec("mCvTagStartMap", mCvTagStartMap); memReporter->AddVec("mCvIPITagStartMap", mCvIPITagStartMap); memReporter->AddVec(mCvStreamSizes); memReporter->AddVec(mCvStreamPtrStartIdxs); memReporter->AddVec(mCvStreamPtrs); memReporter->AddVec(mCvModuleInfo); memReporter->AddVec(mCVSrcFileRefCache); if (mCvHeaderData != NULL) memReporter->Add("mCvHeaderData", mCvStreamSizes[1]); for (auto& entry : mCvTypeSectionData) { if (entry.mSize != -1) memReporter->Add("mCvTypeSectionData", entry.mSize); else memReporter->Add("mCvTypeSectionData", mCvStreamSizes[2]); } if (mCvStrTableData != NULL) memReporter->Add("mCvStrTableData", mCvStreamSizes[mStringTable.mStream]); if (mCvPublicSymbolData != NULL) memReporter->Add("mCvPublicSymbolData", mCvStreamSizes[mCvPublicSymbolInfoStream]); if (mCvGlobalSymbolData != NULL) memReporter->Add("mCvGlobalSymbolData", mCvStreamSizes[mCvGlobalSymbolInfoStream]); //memReporter->AddVec(mCvNamespaceStaticEntries); } bool COFF::TryLoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { bool isVerifyOnly = mDebugger == NULL; if (!mPDBPath.IsEmpty()) return false; // Already have a PDB loaded mCvMappedFile = CreateFileA(pdbPath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL); if (mCvMappedFile == INVALID_HANDLE_VALUE) { return false; } BP_ZONE("COFF::TryLoadPDB"); if (!isVerifyOnly) mDebugger->OutputMessage(StrFormat("Loading PDB %s\n", pdbPath.c_str())); DWORD highFileSize = 0; int fileSize = (int)GetFileSize(mCvMappedFile, &highFileSize); mCvMappedFileMapping = CreateFileMapping(mCvMappedFile, NULL, PAGE_READONLY, 0, fileSize, NULL); mCvMappedViewOfFile = MapViewOfFile(mCvMappedFileMapping, FILE_MAP_READ, 0, 0, fileSize); mCvMappedFileSize = fileSize; //FileStream pdbFS; //if (!pdbFS.Open(pdbPath, "rb")) //return false; mPDBPath = pdbPath; mCvDataStream = new SafeMemStream(mCvMappedViewOfFile, fileSize, false); //Cv_HEADER //pdbFS.Read char sig[32]; mCvDataStream->Read(sig, 32); if (memcmp(sig, MSF_SIGNATURE_700, 32) != 0) { if (!isVerifyOnly) Fail("PDB signature error"); return false; } BfLogDbg("Loading PDB %s\n", pdbPath.c_str()); BP_ZONE_F("LoadCv %s", pdbPath.c_str()); int pageSize = mCvDataStream->ReadInt32(); int fpmPageNum = mCvDataStream->ReadInt32(); int totalPageCount = mCvDataStream->ReadInt32(); int rootDirSize = mCvDataStream->ReadInt32(); int unknown = mCvDataStream->ReadInt32(); int rootDirPtrs[NUM_ROOT_DIRS]; mCvDataStream->ReadT(rootDirPtrs); bool failed = false; mCvPageSize = pageSize; for (int i = 0; i < 31; i++) { if (mCvPageSize < (1 << i)) break; mCvPageBits = i; } int rootPageCount = (rootDirSize + pageSize - 1) / pageSize; int rootPointersPages = (rootPageCount*sizeof(int32) + pageSize - 1) / pageSize; int rootPageIdx = 0; Array rootDirData; rootDirData.Resize(rootPageCount * pageSize); for (int rootDirIdx = 0; rootDirIdx < rootPointersPages; rootDirIdx++) { int rootDirPtr = rootDirPtrs[rootDirIdx]; if (rootDirPtr == 0) continue; mCvDataStream->SetPos(rootDirPtr * pageSize); int32 rootPages[1024]; mCvDataStream->Read(rootPages, pageSize); for (int subRootPageIdx = 0; subRootPageIdx < pageSize / 4; subRootPageIdx++, rootPageIdx++) { if (rootPageIdx >= rootPageCount) break; int rootPagePtr = rootPages[subRootPageIdx]; if (rootPagePtr == 0) break; mCvDataStream->SetPos(rootPagePtr * pageSize); mCvDataStream->Read(&rootDirData[rootPageIdx * pageSize], pageSize); } } int startingTypeIdx = mTypes.size(); if (!ParseCv(*mCvDataStream, &rootDirData[0], pageSize, wantGuid, wantAge)) { //mDebugger->OutputMessage("Failed to parse PDB\n"); return false; } //FixTypes(startingTypeIdx); //MapTypes(startingTypeIdx); if (mCvDataStream->mFailed) return false; //OutputDebugStrF("COFF::TryLoadPDB %s\n", pdbPath.c_str()); if (!isVerifyOnly) mDebugger->ModuleChanged(this); mPDBLoaded = true; return true; } void COFF::ClosePDB() { delete mCvDataStream; mCvDataStream = NULL; delete mCvHeaderData; mCvHeaderData = NULL; delete mCvStrTableData; mCvStrTableData = NULL; for (auto& entry : mCvTypeSectionData) delete entry.mData; mCvTypeSectionData.Clear(); for (auto& entry : mCvCompileUnitData) delete entry.mData; mCvCompileUnitData.Clear(); delete mCvPublicSymbolData; mCvPublicSymbolData = NULL; delete mCvGlobalSymbolData; mCvGlobalSymbolData = NULL; delete mNewFPOData; mNewFPOData = NULL; if (mCvMappedViewOfFile != NULL) ::UnmapViewOfFile(mCvMappedViewOfFile); mCvMappedViewOfFile = NULL; if (mCvMappedFileMapping != INVALID_HANDLE_VALUE) ::CloseHandle(mCvMappedFileMapping); mCvMappedFileMapping = NULL; if (mCvMappedFile != INVALID_HANDLE_VALUE) ::CloseHandle(mCvMappedFile); mCvMappedFile = NULL; for (auto kv : mHotLibMap) delete kv.mValue; mHotLibMap.Clear(); mHotLibSymMap.Clear(); } bool COFF::LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { if (mDebugTarget->mTargetBinary == this) { // If we don't have to load the debug symbols from a remote source or the cache // then we assume we were locally built, meaning we don't attempt to auto-load // any symbols since we assume we already have what we built for all referenced // dlls mDebugTarget->mWasLocallyBuilt = FileExists(pdbPath); } memcpy(mWantPDBGuid, wantGuid, 16); mWantAge = wantAge; mDbgSymRequest = mDebugger->mDbgSymSrv.CreateRequest(mFilePath, pdbPath, wantGuid, wantAge); mDbgSymRequest->SearchLocal(); if (!mDbgSymRequest->mFinalPDBPath.IsEmpty()) { TryLoadPDB(mDbgSymRequest->mFinalPDBPath, wantGuid, wantAge); mDebugger->mDbgSymSrv.ReleaseRequest(mDbgSymRequest); mDbgSymRequest = NULL; } else mMayBeOld = true; String fileName = GetFileName(mFilePath); mWantsAutoLoadDebugInfo = !mDebugTarget->mWasLocallyBuilt; if ((fileName.Equals("KERNEL32.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("KERNELBASE.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("USER32.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("SHELL32.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("GDI32.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("GDI32FULL.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("NTDLL.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("COMDLG32.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("MSVCRT.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("COMBASE.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("UCRTBASE.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("RPCRT4.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("BCRYPTPRIMITIVES.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("SHCORE.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("WIN32U.DLL", String::CompareKind_OrdinalIgnoreCase)) || (fileName.Equals("MSWSOCK.DLL", String::CompareKind_OrdinalIgnoreCase))) { mWantsAutoLoadDebugInfo = false; } /*if (pdbPath.IndexOf('\\') != -1) // Do we have an absolute path at all? { if (TryLoadPDB(pdbPath, wantGuid, wantAge)) return true; ClosePDB(); }*/ //char exePath[MAX_PATH]; //GetModuleFileNameA(NULL, exePath, sizeof(exePath)); /*String checkPath = ::GetFileDir(mFilePath); checkPath += "/"; checkPath += GetFileName(pdbPath); if (!FileNameEquals(checkPath, pdbPath)) { if (TryLoadPDB(checkPath, wantGuid, wantAge)) return true; }*/ return false; } bool COFF::CheckSection(const char* name, uint8* sectionData, int sectionSize) { if (strcmp(name, ".debug$T") == 0) { mDbgFlavor = DbgFlavor_MS; DbgSectionData entry; entry.mData = sectionData; entry.mSize = sectionSize; mCvTypeSectionData.Add(entry); return true; } if (strcmp(name, ".debug$S") == 0) { DbgSectionData entry; entry.mData = sectionData; entry.mSize = sectionSize; mCvCompileUnitData.Add(entry); return true; } return false; } void COFF::ProcessDebugInfo() { BP_ZONE("COFF::ProcessDebugInfo"); if ((!mCvTypeSectionData.IsEmpty()) && (!mCvCompileUnitData.IsEmpty())) { auto linkedModule = (COFF*)GetLinkedModule(); int startingTypeIdx = (int)linkedModule->mTypes.size(); InitCvTypes(); for (auto entry : mCvTypeSectionData) { uint8* data = entry.mData; GET_INTO(uint32, infoType); BF_ASSERT(infoType == CV_SIGNATURE_C13); CvInitStreamRaw(mCvTypeSectionReader, entry.mData + 4, entry.mSize - 4); //ParseTypeData(data, mCvTypeSectionDataSize - sizeof(uint32)); ParseTypeData(mCvTypeSectionReader, 0); } FixTypes(startingTypeIdx); linkedModule->MapTypes(startingTypeIdx); /*std::unordered_set for (int typeIdx = startingTypeIdx; typeIdx < (int)mTypes.size(); typeIdx++) { DbgType* dbgType = linkedModule->mTypes[typeIdx]; if ((!dbgType->mIsDeclaration) && (dbgType->IsCompositeType())) { } }*/ CvCompileUnit* compileUnit = NULL; for (auto entry : mCvCompileUnitData) { compileUnit = ParseCompileUnit(NULL, compileUnit, entry.mData, entry.mSize); } compileUnit->mLanguage = DbgLanguage_Beef; mMasterCompileUnit->mLanguage = DbgLanguage_Beef; MapCompileUnitMethods(compileUnit); mEndTypeIdx = (int)linkedModule->mTypes.size(); } } void COFF::FinishHotSwap() { DbgModule::FinishHotSwap(); mTypeMap.Clear(); } addr_target COFF::EvaluateLocation(DbgSubprogram* dwSubprogram, const uint8* locData, int locDataLen, WdStackFrame* stackFrame, DbgAddrType* outAddrType, bool allowReg) { if (mDbgFlavor == DbgFlavor_GNU) return DbgModule::EvaluateLocation(dwSubprogram, locData, locDataLen, stackFrame, outAddrType, allowReg); addr_target pc = 0; if (stackFrame != NULL) { // Use 'GetSourcePC', which will offset the RSP when we're not at the top positon of the call stack, since RSP will be the // return address in those cases pc = stackFrame->GetSourcePC(); } int inlineDepth = 0; uint8* data = (uint8*)locData; for (int locIdx = 0; locIdx < locDataLen; locIdx++) { uint8* dataStart = data; GET_INTO(uint16, symLen); uint8* dataEnd = data + symLen; GET_INTO(uint16, symType); CV_LVAR_ADDR_RANGE* rangeInfo = NULL; CV_LVAR_ADDR_GAP* gapsInfo = NULL; addr_target result = 0; if (inlineDepth > 0) { if (symType == S_INLINESITE) inlineDepth++; else if (symType == S_INLINESITE_END) inlineDepth--; data = dataEnd; continue; } bool handled = false; switch (symType) { case S_GDATA32: case S_LDATA32: { DATASYM32& dataSym = *(DATASYM32*)dataStart; *outAddrType = DbgAddrType_Target; return GetSectionAddr(dataSym.seg, dataSym.off); } break; case S_BPREL32: { BPRELSYM32* bpRel32 = (BPRELSYM32*)dataStart; #if BF_DBG_32 *outAddrType = DbgAddrType_Target; return stackFrame->mRegisters.mIntRegs.ebp + (int)bpRel32->off; #else *outAddrType = DbgAddrType_Target; return stackFrame->mRegisters.mIntRegs.rbp + (int)bpRel32->off; #endif } break; case S_LTHREAD32: case S_GTHREAD32: { THREADSYM32& threadSym = *(THREADSYM32*)dataStart; int tlsIndex = mDebugger->ReadMemory(mTLSIndexAddr); addr_target tlsEntry = mDebugger->GetTLSOffset(tlsIndex); *outAddrType = DbgAddrType_Target; return threadSym.off + tlsEntry; } break; case S_REGISTER: { REGSYM* regSym = (REGSYM*)dataStart; *outAddrType = DbgAddrType_Register; int regNum = CvConvRegNum(regSym->reg); return regNum; } break; case S_REGREL32: { REGREL32* regRel32 = (REGREL32*)dataStart; *outAddrType = DbgAddrType_Target; int regNum = CvConvRegNum(regRel32->reg); return stackFrame->mRegisters.mIntRegsArray[regNum] + regRel32->off; } break; case S_DEFRANGE_REGISTER: { DEFRANGESYMREGISTER& defRangeReg = *(DEFRANGESYMREGISTER*)dataStart; *outAddrType = DbgAddrType_Register; result = CvConvRegNum(defRangeReg.reg); rangeInfo = &defRangeReg.range; gapsInfo = &defRangeReg.gaps[0]; } break; case S_DEFRANGE_FRAMEPOINTER_REL: { DEFRANGESYMFRAMEPOINTERREL& defRangeFPRel = *(DEFRANGESYMFRAMEPOINTERREL*)dataStart; *outAddrType = DbgAddrType_Target; #ifdef BF_DBG_64 if (dwSubprogram->mLocalBaseReg == DbgSubprogram::LocalBaseRegKind_RSP) result = stackFrame->mRegisters.mIntRegsArray[X64Reg_RSP] + defRangeFPRel.offFramePointer; else if (dwSubprogram->mLocalBaseReg == DbgSubprogram::LocalBaseRegKind_R13) result = stackFrame->mRegisters.mIntRegsArray[X64Reg_R13] + defRangeFPRel.offFramePointer; else result = stackFrame->mRegisters.mIntRegsArray[X64Reg_RBP] + defRangeFPRel.offFramePointer; #else if (dwSubprogram->mLocalBaseReg == DbgSubprogram::LocalBaseRegKind_ESP) //result = stackFrame->mRegisters.mIntRegsArray[X86Reg_ESP] + BF_ALIGN(dwSubprogram->mFrameBaseLen, 16) + defRangeFPRel.offFramePointer; result = stackFrame->mRegisters.mIntRegsArray[X86Reg_ESP] + dwSubprogram->mFrameBaseLen + defRangeFPRel.offFramePointer; else if (dwSubprogram->mLocalBaseReg == DbgSubprogram::LocalBaseRegKind_EBX) result = stackFrame->mRegisters.mIntRegsArray[X86Reg_EBX] + defRangeFPRel.offFramePointer; else result = stackFrame->mRegisters.mIntRegsArray[X86Reg_EBP] + defRangeFPRel.offFramePointer; #endif rangeInfo = &defRangeFPRel.range; gapsInfo = &defRangeFPRel.gaps[0]; } break; case S_DEFRANGE_SUBFIELD_REGISTER: { DEFRANGESYMSUBFIELDREGISTER& defRangeSubfieldReg = *(DEFRANGESYMSUBFIELDREGISTER*)dataStart; *outAddrType = DbgAddrType_Target; int regNum = CvConvRegNum(defRangeSubfieldReg.reg); result = stackFrame->mRegisters.mIntRegsArray[regNum] + defRangeSubfieldReg.offParent; rangeInfo = &defRangeSubfieldReg.range; gapsInfo = &defRangeSubfieldReg.gaps[0]; } break; case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: { DEFRANGESYMFRAMEPOINTERREL_FULL_SCOPE& defFPRel = *(DEFRANGESYMFRAMEPOINTERREL_FULL_SCOPE*)dataStart; #ifdef BF_DBG_64 int regNum = X64Reg_RSP; #else int regNum = X86Reg_ESP; #endif *outAddrType = DbgAddrType_Target; result = stackFrame->mRegisters.mIntRegsArray[regNum] + defFPRel.offFramePointer; return result; } break; case S_DEFRANGE_REGISTER_REL: { DEFRANGESYMREGISTERREL& defRangeRegRel = *(DEFRANGESYMREGISTERREL*)dataStart; *outAddrType = DbgAddrType_Target; int regNum = CvConvRegNum(defRangeRegRel.baseReg); result = stackFrame->mRegisters.mIntRegsArray[regNum] + defRangeRegRel.offBasePointer; rangeInfo = &defRangeRegRel.range; gapsInfo = &defRangeRegRel.gaps[0]; } break; case S_PROCREF: { // Not currently handled. } break; case S_INLINESITE: { inlineDepth++; } break; default: BF_FATAL("Not handled"); } if (rangeInfo != NULL) { auto rangeStart = GetSectionAddr(rangeInfo->isectStart, rangeInfo->offStart); if ((pc >= rangeStart) && (pc < rangeStart + rangeInfo->cbRange)) { bool inRange = true; while ((uint8*)gapsInfo < (uint8*)dataEnd) { if ((pc >= rangeStart + gapsInfo->gapStartOffset) && (pc < rangeStart + gapsInfo->gapStartOffset + gapsInfo->cbRange)) { inRange = false; break; } gapsInfo++; } if (inRange) return result; } } data = dataEnd; } if ((dwSubprogram != NULL) && (dwSubprogram->mIsOptimized)) *outAddrType = DbgAddrType_OptimizedOut; else *outAddrType = DbgAddrType_NoValue; return 0; } bool COFF::CanGetOldSource() { return mCvSrcSrvStream != -1; } String COFF::GetOldSourceCommand(const StringImpl& path) { if (mCvSrcSrvStream == -1) return ""; int outSize; uint8* data = CvReadStream(mCvSrcSrvStream, &outSize); String cmdBlock = String((char*)data, outSize); delete data; bool inFileSection = false; Dictionary defs; enum _SectType { _SectType_None, _SectType_Ini, _SectType_Variables, _SectType_SourceFiles }; _SectType sectType = _SectType_None; // { AutoCrit autoCrit(gDebugManager->mCritSect); defs["TARG"] = gDebugManager->mSymSrvOptions.mSourceServerCacheDir; } std::function _Expand = [&](String& str) { for (int i = 0; i < str.length(); i++) { if (str[i] == '%') { int endIdx = str.IndexOf('%', i + 1); if (endIdx != -1) { String varName = ToUpper(str.Substring(i + 1, endIdx - i - 1)); if (((endIdx < str.length() - 1) && (str[endIdx + 1] == '(')) && ((varName == "FNVAR") || (varName == "FNBKSL") || (varName == "FNFILE"))) { int closePos = str.IndexOf(')', endIdx + 2); if (closePos != -1) { String paramStr = str.Substring(endIdx + 2, closePos - endIdx - 2); _Expand(paramStr); if (varName == "FNVAR") { paramStr = defs[paramStr]; } else { if (varName == "FNBKSL") { paramStr.Replace("/", "\\"); } else if (varName == "FNFILE") { paramStr = GetFileName(paramStr); } } str.Remove(i, closePos - i + 1); str.Insert(i, paramStr); i--; } } else { auto replaceStr = defs[varName]; if (!replaceStr.IsEmpty()) { str.Remove(i, endIdx - i + 1); str.Insert(i, replaceStr); i--; } } } } } }; int linePos = 0; while (true) { int crPos = cmdBlock.IndexOf('\n', linePos); if (crPos == -1) break; int lineEndPos = crPos; if (cmdBlock[lineEndPos - 1] == '\r') lineEndPos--; String line = cmdBlock.Substring(linePos, lineEndPos - linePos); if (line.StartsWith("SRCSRV: ini -")) { sectType = _SectType_Ini; } else if (line.StartsWith("SRCSRV: variables -")) { sectType = _SectType_Variables; } else if (line.StartsWith("SRCSRV: source files -")) { sectType = _SectType_SourceFiles; } else if (line.StartsWith("SRCSRV: end -")) { break; } else { switch (sectType) { case _SectType_Ini: case _SectType_Variables: { int eqPos = line.IndexOf('='); if (eqPos != -1) { defs[ToUpper(line.Substring(0, eqPos))] = line.Substring(eqPos + 1); } break; } case _SectType_SourceFiles: { int curStrIdx = 0; int curStrPos = 0; StringT<256> var; bool matches = false; while (curStrPos < (int)line.length()) { int starPos = line.IndexOf('*', curStrPos); if (starPos == -1) starPos = line.length(); //var = line.Substring(curStrPos, starPos - curStrPos); var.Clear(); var.Insert(0, line.c_str() + curStrPos, starPos - curStrPos); if (curStrIdx == 0) { matches = FileNameEquals(var, path); if (!matches) break; } defs[StrFormat("VAR%d", curStrIdx + 1)] = var; curStrIdx++; curStrPos = starPos + 1; } if (!matches) break; String target = defs["SRCSRVTRG"]; String cmd = defs["SRCSRVCMD"]; String env = defs["SRCSRVENV"]; _Expand(target); _Expand(cmd); _Expand(env); String retVal; if ((cmd.IsEmpty()) && (target.StartsWith("HTTP", StringImpl::CompareKind_OrdinalIgnoreCase))) { String localFile; BfpFileResult result; BFP_GETSTR_HELPER(localFile, result, BfpFile_GetTempPath(__STR, __STRLEN, &result)); int dotPos = target.IndexOf("://"); if (dotPos != -1) { localFile.Append("SymbolCache\\src\\"); localFile.Append(StringView(target, dotPos + 3)); localFile.Replace("/", "\\"); } retVal = localFile; retVal += "\n"; retVal += target; retVal += "\n"; retVal += env; } else if (!cmd.IsWhitespace()) { retVal = target; retVal += "\n"; retVal += cmd; retVal += "\n"; retVal += env; } return retVal; } break; } } linePos = crPos + 1; } return ""; } bool COFF::HasPendingDebugInfo() { if (mDbgSymRequest == NULL) return false; return true; } void COFF::PreCacheImage() { if (!mDebugger->IsMiniDumpDebugger()) return; if (mLoadState != DbgModuleLoadState_NotLoaded) return; auto miniDumpDebugger = (MiniDumpDebugger*)mDebugger; if (mOrigImageData != NULL) return; mDebugger->mDbgSymSrv.PreCacheImage(mFilePath, mTimeStamp, mImageSize); } void COFF::PreCacheDebugInfo() { if (mDbgSymRequest == NULL) return; if (mDbgSymRequest->mInProcess) return; bool hasDebugInfo = false; mDbgSymRequest->SearchCache(); if (mDbgSymRequest->mFinalPDBPath.IsEmpty()) { if (GetCurrentThreadId() == mDebugger->mDebuggerThreadId) { mDbgSymRequest->mIsPreCache = true; mDbgSymRequest->SearchSymSrv(); mDbgSymRequest->mIsPreCache = false; mDbgSymRequest->mFailed = false; } } } bool COFF::LoadModuleImage(const StringImpl& imagePath) { if (!mDebugger->IsMiniDumpDebugger()) return false; auto miniDumpDebugger = (MiniDumpDebugger*)mDebugger; if (!imagePath.IsEmpty()) { MappedFile* mappedFile = miniDumpDebugger->MapModule(this, imagePath); mMappedImageFile = mappedFile; if (mappedFile != NULL) { MemStream memStream(mappedFile->mData, mappedFile->mFileSize, false); ReadCOFF(&memStream, false); mOrigImageData = new DbgModuleMemoryCache(mImageBase, mImageSize); } } if (mOrigImageData == NULL) // Failed? { mLoadState = DbgModuleLoadState_Failed; return false; } return true; } bool COFF::RequestImage() { if (!mDebugger->IsMiniDumpDebugger()) return false; if (mLoadState != DbgModuleLoadState_NotLoaded) return false; auto miniDumpDebugger = (MiniDumpDebugger*)mDebugger; if (mOrigImageData != NULL) return false; if (GetCurrentThreadId() == mDebugger->mDebuggerThreadId) { auto prevRunState = mDebugger->mRunState; mDebugger->mRunState = RunState_SearchingSymSrv; mDebugger->mDebugManager->mOutMessages.push_back(StrFormat("symsrv Searching for image '%s'", mFilePath.c_str())); auto dbgSymRequest = mDebugger->mDbgSymSrv.CreateRequest(); mDebugger->mActiveSymSrvRequest = dbgSymRequest; BF_ASSERT(mDebugger->mDebugManager->mCritSect.mLockCount == 1); mDebugger->mDebugManager->mCritSect.Unlock(); // We unlock to allow the IDE to continue updating while we search String imagePath = dbgSymRequest->SearchForImage(mFilePath, mTimeStamp, mImageSize); mDebugger->mDebugManager->mCritSect.Lock(); delete mDebugger->mActiveSymSrvRequest; mDebugger->mActiveSymSrvRequest = NULL; mDebugger->mRunState = prevRunState; return LoadModuleImage(imagePath); } else { return false; } return false; } bool COFF::RequestDebugInfo(bool allowRemote) { if (mDbgSymRequest == NULL) return false; if (mDbgSymRequest->mInProcess) return false; bool hasDebugInfo = false; mDbgSymRequest->SearchCache(); if (mDbgSymRequest->mFinalPDBPath.IsEmpty()) { if (!allowRemote) return false; if (GetCurrentThreadId() == mDebugger->mDebuggerThreadId) { auto prevRunState = mDebugger->mRunState; mDebugger->mRunState = RunState_SearchingSymSrv; mDbgSymRequest->mInProcess = true; mDebugger->mActiveSymSrvRequest = mDbgSymRequest; BF_ASSERT(mDebugger->mDebugManager->mCritSect.mLockCount == 1); mDebugger->mDebugManager->mCritSect.Unlock(); mDbgSymRequest->SearchSymSrv(); mDebugger->mDebugManager->mCritSect.Lock(); mDebugger->mActiveSymSrvRequest = NULL; mDbgSymRequest->mInProcess = false; mDebugger->mRunState = prevRunState; } else { return false; } } if (!mDbgSymRequest->mFinalPDBPath.IsEmpty()) hasDebugInfo = TryLoadPDB(mDbgSymRequest->mFinalPDBPath, mDbgSymRequest->mWantGuid, mDbgSymRequest->mWantAge); mDebugger->mDbgSymSrv.ReleaseRequest(mDbgSymRequest); mDbgSymRequest = NULL; return hasDebugInfo; } bool COFF::WantsAutoLoadDebugInfo() { return mWantsAutoLoadDebugInfo; } bool COFF::DbgIsStrMutable(const char* str) { if (mIsHotObjectFile) { return GetLinkedModule()->DbgIsStrMutable(str); } if (mCvMappedViewOfFile == NULL) return true; if ((str >= (const char*)mCvMappedViewOfFile) && (str < (const char*)mCvMappedViewOfFile + mCvMappedFileSize)) return false; return true; } // We don't need this for Beef linkining because of "FORCELINK_", but we DO need it for // libraries we linked to, either in the DLL or in the LIB addr_target COFF::LocateSymbol(const StringImpl& name) { BP_ZONE("COFF::LocateSymbol"); BfLogDbg("COFF:LocateSymbol '%s'\n", name.c_str()); if (mHotLibMap.IsEmpty()) { for (auto moduleInfo : mCvModuleInfo) { if (moduleInfo->mObjectName == NULL) continue; String libName = moduleInfo->mObjectName; if (!libName.EndsWith(".lib", StringImpl::CompareKind_OrdinalIgnoreCase)) continue; CvLibInfo** libInfoPtr; if (!mHotLibMap.TryAdd(libName, NULL, &libInfoPtr)) continue; CvLibInfo* libInfo = new CvLibInfo(); *libInfoPtr = libInfo; if (!libInfo->mLibFile.Init(libName, false)) continue; for (auto kv : libInfo->mLibFile.mOldEntries) { auto libEntry = kv.mValue; while (libEntry != NULL) { for (auto sym : libEntry->mSymbols) { #ifdef BF_DBG_32 if (sym.StartsWith('_')) mHotLibSymMap[sym.Substring(1)] = libEntry; else // Fallthrough #endif mHotLibSymMap[sym] = libEntry; } libEntry = libEntry->mNextWithSameName; } } } } BeLibEntry* libEntry; if (!mHotLibSymMap.TryGetValue(name, &libEntry)) return 0; if (libEntry->mLength == -1) { BfLogDbg("Already loaded obj '%s' in '%s'\n", libEntry->mName.c_str(), libEntry->mLibFile->mFilePath.c_str()); // We already tried to load this return 0; } auto fileExt = GetFileExtension(libEntry->mName); if (String::Equals(fileExt, ".dll", StringImpl::CompareKind_OrdinalIgnoreCase)) { for (auto dbgModule : mDebugTarget->mDbgModules) { if (String::Equals(libEntry->mName, dbgModule->mDisplayName)) { dbgModule->ParseSymbolData(); auto entry = dbgModule->mSymbolNameMap.Find(name.c_str()); if (entry == NULL) continue; addr_target callTarget; #ifdef BF_DBG_32 callTarget = entry->mValue->mAddress; #else BfLogDbg("Setting up DLL thunk for '%s' in '%s'\n", name.c_str(), dbgModule->mFilePath.c_str()); int wantHotSize = 16; if (mHotThunkDataLeft < wantHotSize) { int allocSize = 4096; mDebugger->ReserveHotTargetMemory(allocSize); int outAllocSize = 0; mHotThunkCurAddr = mDebugger->AllocHotTargetMemory(allocSize, true, false, &outAllocSize); mHotThunkDataLeft = outAllocSize; } int outAllocSize = 0; addr_target hotAddr = mHotThunkCurAddr; mHotThunkCurAddr += wantHotSize; mHotThunkDataLeft -= wantHotSize; #pragma pack(push, 1) struct HotLongJmpOp { uint8 mOpCode0; uint8 mOpCode1; int32 mRIPRel; uint64 mTarget; }; #pragma pack(pop) HotLongJmpOp jumpOp; jumpOp.mOpCode0 = 0xFF; jumpOp.mOpCode1 = 0x25; jumpOp.mRIPRel = 0; jumpOp.mTarget = entry->mValue->mAddress; mDebugger->WriteMemory(hotAddr, jumpOp); callTarget = hotAddr; #endif char* dupStr = (char*)mAlloc.AllocBytes(name.length() + 1); memcpy(dupStr, name.c_str(), name.length()); DbgSymbol* dwSymbol = mAlloc.Alloc(); dwSymbol->mDbgModule = this; dwSymbol->mName = dupStr; dwSymbol->mAddress = callTarget; mSymbolNameMap.Insert(dwSymbol); return callTarget; } } return 0; } BfLogDbg("Loading obj '%s' in '%s'\n", libEntry->mName.c_str(), libEntry->mLibFile->mFilePath.c_str()); // #ifdef _DEBUG // FILE* fpTest = fopen("c:\\temp\\locateSym.obj", "wb"); // // uint8* data = new uint8[libEntry->mLength]; // // fseek(libEntry->mLibFile->mOldFileStream.mFP, libEntry->mOldDataPos + sizeof(BeLibMemberHeader), SEEK_SET); // fread(data, 1, libEntry->mLength, libEntry->mLibFile->mOldFileStream.mFP); // fwrite(data, 1, libEntry->mLength, fpTest); // fclose(fpTest); // delete data; // #endif FileSubStream fileStream; fileStream.mFP = libEntry->mLibFile->mOldFileStream.mFP; fileStream.mOffset = libEntry->mOldDataPos + sizeof(BeLibMemberHeader); fileStream.mSize = libEntry->mLength; fileStream.SetPos(0); DbgModule* dbgModule = new COFF(mDebugger->mDebugTarget); dbgModule->mHotIdx = mDebugger->mActiveHotIdx; dbgModule->mFilePath = libEntry->mName + "@" + libEntry->mLibFile->mFilePath; bool success = dbgModule->ReadCOFF(&fileStream, true); fileStream.mFP = NULL; // Mark as loaded libEntry->mLength = -1; if (!success) { BfLogDbg("Failed\n"); Fail(StrFormat("Debugger failed to read binary '%s' in '%s'", libEntry->mName.c_str(), libEntry->mLibFile->mFilePath.c_str())); delete dbgModule; return 0; } mDebugger->mDebugTarget->mDbgModules.push_back(dbgModule); auto symbolEntry = mSymbolNameMap.Find(name.c_str()); if (symbolEntry != NULL) return symbolEntry->mValue->mAddress; return 0; } void COFF::ParseFrameDescriptors(uint8* data, int size, addr_target baseAddr) { uint8* ptr = data; uint8* endPtr = data + size; Dictionary programMap; Array cmds; while (ptr < endPtr) { COFFFrameDescriptor* frame = (COFFFrameDescriptor*)ptr; ptr += sizeof(COFFFrameDescriptor); COFFFrameDescriptorEntry entry; entry.mFrameDescriptor = frame; bool failed = false; COFFFrameProgram* programPtr = NULL; if (programMap.TryAdd(frame->mFrameFunc, NULL, &programPtr)) { COFFFrameProgram program; cmds.Clear(); String curCmd; const char* ptr = mStringTable.mStrTable + frame->mFrameFunc; while (true) { char c = *(ptr++); if ((c == ' ') || (c == 0)) { if (curCmd == "$eip") cmds.Add(COFFFrameProgram::Command_EIP); else if ((curCmd == "$esp") || (curCmd == "$21")) cmds.Add(COFFFrameProgram::Command_ESP); else if ((curCmd == "$ebp") || (curCmd == "$22")) cmds.Add(COFFFrameProgram::Command_EBP); else if ((curCmd == "$eax") || (curCmd == "$17")) cmds.Add(COFFFrameProgram::Command_EAX); else if ((curCmd == "$ebx") || (curCmd == "$20")) cmds.Add(COFFFrameProgram::Command_EBX); else if ((curCmd == "$ecx") || (curCmd == "$18")) cmds.Add(COFFFrameProgram::Command_ECX); else if ((curCmd == "$edx") || (curCmd == "$19")) cmds.Add(COFFFrameProgram::Command_EDX); else if ((curCmd == "$esi") || (curCmd == "$23")) cmds.Add(COFFFrameProgram::Command_ESI); else if ((curCmd == "$edi") || (curCmd == "$24")) cmds.Add(COFFFrameProgram::Command_EDI); else if (curCmd == "$T0") cmds.Add(COFFFrameProgram::Command_T0); else if (curCmd == "$T1") cmds.Add(COFFFrameProgram::Command_T1); else if (curCmd == "$T2") cmds.Add(COFFFrameProgram::Command_T2); else if (curCmd == "$T3") cmds.Add(COFFFrameProgram::Command_T3); else if ((curCmd == ".raSearch") || (curCmd == ".raSearchStart")) cmds.Add(COFFFrameProgram::Command_RASearch); else if (curCmd == "+") cmds.Add(COFFFrameProgram::Command_Add); else if (curCmd == "-") cmds.Add(COFFFrameProgram::Command_Subtract); else if (curCmd == "@") cmds.Add(COFFFrameProgram::Command_Align); else if (curCmd == "=") cmds.Add(COFFFrameProgram::Command_Set); else if (curCmd == "^") cmds.Add(COFFFrameProgram::Command_Deref); else if (!curCmd.IsEmpty()) { if ((curCmd[0] >= '0') && (curCmd[0] <= '9')) { int val = atoi(curCmd.c_str()); if ((val >= 0) && (val <= 255)) { cmds.Add(COFFFrameProgram::Command_Value8); cmds.Add((COFFFrameProgram::Command)val); } else { cmds.Add(COFFFrameProgram::Command_Value); cmds.Insert(cmds.size(), (COFFFrameProgram::Command*)&val, 4); } } else { failed = true; Fail(StrFormat("Invalid COFF frame program: %s", curCmd.c_str())); } } if (c == 0) break; curCmd.Clear(); } else curCmd.Append(c); } if (failed) cmds.Clear(); cmds.Add(COFFFrameProgram::Command_None); program.mCommands = (COFFFrameProgram::Command*)mAlloc.AllocBytes(cmds.size()); memcpy(program.mCommands, &cmds[0], cmds.size()); *programPtr = program; } entry.mProgram = *programPtr; mDebugTarget->mCOFFFrameDescriptorMap.insert(std::make_pair(baseAddr + frame->mRvaStart, entry)); } mParsedFrameDescriptors = true; } void COFF::ParseFrameDescriptors() { if (mParsedFrameDescriptors) return; if (mCvNewFPOStream == 0) return; int streamSize = 0; mNewFPOData = CvReadStream(mCvNewFPOStream, &streamSize); ParseFrameDescriptors(mNewFPOData, streamSize, mImageBase); } NS_BF_DBG_BEGIN // void TestCoff(void* tdata, int tdataSize, void* cuData, int cuDataSize) // { // DebugTarget* debugTarget = new DebugTarget(NULL); // { // COFF coff(debugTarget); // coff.mCvTypeSectionData = (uint8*)tdata; // coff.mCvTypeSectionDataSize = tdataSize; // // coff.mCvCompileUnitData = (uint8*)cuData; // coff.mCvCompileUnitDataSize = cuDataSize; // // coff.ProcessDebugInfo(); // } // delete debugTarget; // } void TestPDB(const String& fileName) { DebugTarget* debugTarget = new DebugTarget(NULL); COFF coff(debugTarget); uint8 wantGuid[16]; coff.LoadPDB(fileName, wantGuid, -1); coff.CvParseIPI(); coff.ParseGlobalsData(); coff.ParseSymbolData(); for (int compileUnitId = 0; compileUnitId < (int)coff.mCvModuleInfo.size(); compileUnitId++) coff.ParseCompileUnit(compileUnitId); } NS_BF_DBG_END