// This file is distributed under a BSD license. See LICENSE.txt for details. #include "_types.hpp" #include "debuginfo.hpp" #include "pdbfile.hpp" #include #include "windows.h" #include "ole2.h" /****************************************************************************/ // I don't want to use the complete huge dia2.h headers (>350k), so here // goes the minimal version... enum SymTagEnum { SymTagNull, SymTagExe, SymTagCompiland, SymTagCompilandDetails, SymTagCompilandEnv, SymTagFunction, SymTagBlock, SymTagData, SymTagAnnotation, SymTagLabel, SymTagPublicSymbol, SymTagUDT, SymTagEnum, SymTagFunctionType, SymTagPointerType, SymTagArrayType, SymTagBaseType, SymTagTypedef, SymTagBaseClass, SymTagFriend, SymTagFunctionArgType, SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagUsingNamespace, SymTagVTableShape, SymTagVTable, SymTagCustom, SymTagThunk, SymTagCustomType, SymTagManagedType, SymTagDimension, SymTagMax }; class IDiaEnumSymbols; class IDiaEnumSymbolsByAddr; class IDiaEnumTables; class IDiaDataSource; class IDiaSession; class IDiaSymbol; class IDiaSectionContrib; class IDiaTable; // not transcribed here: class IDiaSourceFile; class IDiaEnumSourceFiles; class IDiaEnumLineNumbers; class IDiaEnumDebugStreams; class IDiaEnumInjectedSources; class IDiaEnumSymbols : public IUnknown { public: virtual HRESULT __stdcall get__NewEnum(IUnknown **ret) = 0; virtual HRESULT __stdcall get_Count(LONG *ret) = 0; virtual HRESULT __stdcall Item(DWORD index,IDiaSymbol **symbol) = 0; virtual HRESULT __stdcall Next(ULONG celt,IDiaSymbol **rgelt,ULONG *pceltFetched) = 0; virtual HRESULT __stdcall Skip(ULONG celt) = 0; virtual HRESULT __stdcall Reset() = 0; virtual HRESULT __stdcall Clone(IDiaEnumSymbols **penum) = 0; }; class IDiaEnumSymbolsByAddr : public IUnknown { public: virtual HRESULT __stdcall symbolByAddr(DWORD isect,DWORD offset,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall symbolByRVA(DWORD relativeVirtualAddress,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall symbolByVA(ULONGLONG virtualAddress,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall Next(ULONG celt,IDiaSymbol ** rgelt,ULONG* pceltFetched) = 0; virtual HRESULT __stdcall Prev(ULONG celt,IDiaSymbol ** rgelt,ULONG * pceltFetched) = 0; virtual HRESULT __stdcall Clone(IDiaEnumSymbolsByAddr **ppenum) = 0; }; class IDiaEnumTables : public IUnknown { public: virtual HRESULT __stdcall get__NewEnum(IUnknown **ret) = 0; virtual HRESULT __stdcall get_Count(LONG *ret) = 0; virtual HRESULT __stdcall Item(VARIANT index,IDiaTable **table) = 0; virtual HRESULT __stdcall Next(ULONG celt,IDiaTable ** rgelt,ULONG *pceltFetched) = 0; virtual HRESULT __stdcall Skip(ULONG celt) = 0; virtual HRESULT __stdcall Reset() = 0; virtual HRESULT __stdcall Clone(IDiaEnumTables **ppenum) = 0; }; class IDiaDataSource : public IUnknown { public: virtual HRESULT __stdcall get_lastError(BSTR *ret) = 0; virtual HRESULT __stdcall loadDataFromPdb(LPCOLESTR pdbPath) = 0; virtual HRESULT __stdcall loadAndValidateDataFromPdb(LPCOLESTR pdbPath,GUID *pcsig70,DWORD sig,DWORD age) = 0; virtual HRESULT __stdcall loadDataForExe(LPCOLESTR executable,LPCOLESTR searchPath,IUnknown *pCallback) = 0; virtual HRESULT __stdcall loadDataFromIStream(IStream *pIStream) = 0; virtual HRESULT __stdcall openSession(IDiaSession **ppSession) = 0; }; class IDiaSession : public IUnknown { public: virtual HRESULT __stdcall get_loadAddress(ULONGLONG *ret) = 0; virtual HRESULT __stdcall put_loadAddress(ULONGLONG val) = 0; virtual HRESULT __stdcall get_globalScape(IDiaSymbol **sym) = 0; virtual HRESULT __stdcall getEnumTables(IDiaEnumTables** ppEnumTables) = 0; virtual HRESULT __stdcall getSymbolsByAddr(IDiaEnumSymbolsByAddr** ppEnumbyAddr) = 0; virtual HRESULT __stdcall findChildren(IDiaSymbol* parent,enum SymTagEnum symtag,LPCOLESTR name,DWORD compareFlags,IDiaEnumSymbols** ppResult) = 0; virtual HRESULT __stdcall findSymbolByAddr(DWORD isect,DWORD offset,enum SymTagEnum symtag,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall findSymbolByRVA(DWORD rva,enum SymTagEnum symtag,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall findSymbolByVA(ULONGLONG va,enum SymTagEnum symtag,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall findSymbolByToken(ULONG token,enum SymTagEnum symtag,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall symsAreEquiv(IDiaSymbol* symbolA,IDiaSymbol* symbolB) = 0; virtual HRESULT __stdcall symbolById(DWORD id,IDiaSymbol** ppSymbol) = 0; virtual HRESULT __stdcall findSymbolByRVAEx(DWORD rva,enum SymTagEnum symtag,IDiaSymbol** ppSymbol,long* displacement) = 0; virtual HRESULT __stdcall findSymbolByVAEx(ULONGLONG va,enum SymTagEnum symtag,IDiaSymbol** ppSymbol,long* displacement) = 0; virtual HRESULT __stdcall findFile(IDiaSymbol* pCompiland,LPCOLESTR name,DWORD compareFlags,IDiaEnumSourceFiles** ppResult) = 0; virtual HRESULT __stdcall findFileById(DWORD uniqueId,IDiaSourceFile** ppResult) = 0; virtual HRESULT __stdcall findLines(IDiaSymbol* compiland,IDiaSourceFile* file,IDiaEnumLineNumbers** ppResult) = 0; virtual HRESULT __stdcall findLinesByAddr(DWORD seg,DWORD offset,DWORD length,IDiaEnumLineNumbers** ppResult) = 0; virtual HRESULT __stdcall findLinesByRVA(DWORD rva,DWORD length,IDiaEnumLineNumbers** ppResult) = 0; virtual HRESULT __stdcall findLinesByVA(ULONGLONG va,DWORD length,IDiaEnumLineNumbers** ppResult) = 0; virtual HRESULT __stdcall findLinesByLinenum(IDiaSymbol* compiland,IDiaSourceFile* file,DWORD linenum,DWORD column,IDiaEnumLineNumbers** ppResult) = 0; virtual HRESULT __stdcall findInjectedSource(LPCOLESTR srcFile,IDiaEnumInjectedSources** ppResult) = 0; virtual HRESULT __stdcall getEnumDebugStreams(IDiaEnumDebugStreams** ppEnumDebugStreams) = 0; }; class IDiaSymbol : public IUnknown { public: virtual HRESULT __stdcall get_symIndexId(DWORD *ret) = 0; virtual HRESULT __stdcall get_symTag(DWORD *ret) = 0; virtual HRESULT __stdcall get_name(BSTR *ret) = 0; virtual HRESULT __stdcall get_lexicalParent(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_classParent(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_type(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_dataKind(DWORD *ret) = 0; virtual HRESULT __stdcall get_locationType(DWORD *ret) = 0; virtual HRESULT __stdcall get_addressSection(DWORD *ret) = 0; virtual HRESULT __stdcall get_addressOffset(DWORD *ret) = 0; virtual HRESULT __stdcall get_relativeVirtualAddress(DWORD *ret) = 0; virtual HRESULT __stdcall get_virtualAddress(ULONGLONG *ret) = 0; virtual HRESULT __stdcall get_registerId(DWORD *ret) = 0; virtual HRESULT __stdcall get_offset(LONG *ret) = 0; virtual HRESULT __stdcall get_length(ULONGLONG *ret) = 0; virtual HRESULT __stdcall get_slot(DWORD *ret) = 0; virtual HRESULT __stdcall get_volatileType(BOOL *ret) = 0; virtual HRESULT __stdcall get_constType(BOOL *ret) = 0; virtual HRESULT __stdcall get_unalignedType(BOOL *ret) = 0; virtual HRESULT __stdcall get_access(DWORD *ret) = 0; virtual HRESULT __stdcall get_libraryName(BSTR *ret) = 0; virtual HRESULT __stdcall get_platform(DWORD *ret) = 0; virtual HRESULT __stdcall get_language(DWORD *ret) = 0; virtual HRESULT __stdcall get_editAndContinueEnabled(BOOL *ret) = 0; virtual HRESULT __stdcall get_frontEndMajor(DWORD *ret) = 0; virtual HRESULT __stdcall get_frontEndMinor(DWORD *ret) = 0; virtual HRESULT __stdcall get_frontEndBuild(DWORD *ret) = 0; virtual HRESULT __stdcall get_backEndMajor(DWORD *ret) = 0; virtual HRESULT __stdcall get_backEndMinor(DWORD *ret) = 0; virtual HRESULT __stdcall get_backEndBuild(DWORD *ret) = 0; virtual HRESULT __stdcall get_sourceFileName(BSTR *ret) = 0; virtual HRESULT __stdcall get_unused(BSTR *ret) = 0; virtual HRESULT __stdcall get_thunkOrdinal(DWORD *ret) = 0; virtual HRESULT __stdcall get_thisAdjust(LONG *ret) = 0; virtual HRESULT __stdcall get_virtualBaseOffset(DWORD *ret) = 0; virtual HRESULT __stdcall get_virtual(BOOL *ret) = 0; virtual HRESULT __stdcall get_intro(BOOL *ret) = 0; virtual HRESULT __stdcall get_pure(BOOL *ret) = 0; virtual HRESULT __stdcall get_callingConvention(DWORD *ret) = 0; virtual HRESULT __stdcall get_value(VARIANT *ret) = 0; virtual HRESULT __stdcall get_baseType(DWORD *ret) = 0; virtual HRESULT __stdcall get_token(DWORD *ret) = 0; virtual HRESULT __stdcall get_timeStamp(DWORD *ret) = 0; virtual HRESULT __stdcall get_guid(GUID *ret) = 0; virtual HRESULT __stdcall get_symbolsFileName(BSTR *ret) = 0; virtual HRESULT __stdcall get_reference(BOOL *ret) = 0; virtual HRESULT __stdcall get_count(DWORD *ret) = 0; virtual HRESULT __stdcall get_bitPosition(DWORD *ret) = 0; virtual HRESULT __stdcall get_arrayIndexType(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_packed(BOOL *ret) = 0; virtual HRESULT __stdcall get_constructor(BOOL *ret) = 0; virtual HRESULT __stdcall get_overloadedOperator(BOOL *ret) = 0; virtual HRESULT __stdcall get_nested(BOOL *ret) = 0; virtual HRESULT __stdcall get_hasNestedTypes(BOOL *ret) = 0; virtual HRESULT __stdcall get_hasAssignmentOperator(BOOL *ret) = 0; virtual HRESULT __stdcall get_hasCastOperator(BOOL *ret) = 0; virtual HRESULT __stdcall get_scoped(BOOL *ret) = 0; virtual HRESULT __stdcall get_virtualBaseClass(BOOL *ret) = 0; virtual HRESULT __stdcall get_indirectVirtualBaseClass(BOOL *ret) = 0; virtual HRESULT __stdcall get_virtualBasePointerOffset(LONG *ret) = 0; virtual HRESULT __stdcall get_virtualTableShape(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_lexicalParentId(DWORD *ret) = 0; virtual HRESULT __stdcall get_classParentId(DWORD *ret) = 0; virtual HRESULT __stdcall get_typeId(DWORD *ret) = 0; virtual HRESULT __stdcall get_arrayIndexTypeId(DWORD *ret) = 0; virtual HRESULT __stdcall get_virtualTableShapeId(DWORD *ret) = 0; virtual HRESULT __stdcall get_code(BOOL *ret) = 0; virtual HRESULT __stdcall get_function(BOOL *ret) = 0; virtual HRESULT __stdcall get_managed(BOOL *ret) = 0; virtual HRESULT __stdcall get_msil(BOOL *ret) = 0; virtual HRESULT __stdcall get_virtualBaseDispIndex(DWORD *ret) = 0; virtual HRESULT __stdcall get_undecoratedName(BSTR *ret) = 0; virtual HRESULT __stdcall get_age(DWORD *ret) = 0; virtual HRESULT __stdcall get_signature(DWORD *ret) = 0; virtual HRESULT __stdcall get_compilerGenerated(BOOL *ret) = 0; virtual HRESULT __stdcall get_addressTaken(BOOL *ret) = 0; virtual HRESULT __stdcall get_rank(DWORD *ret) = 0; virtual HRESULT __stdcall get_lowerBound(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_upperBound(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_lowerBoundId(DWORD *ret) = 0; virtual HRESULT __stdcall get_upperBoundId(DWORD *ret) = 0; virtual HRESULT __stdcall get_dataBytes(DWORD cbData,DWORD *pcbData,BYTE data[]) = 0; virtual HRESULT __stdcall findChildren(enum SymTagEnum symtag,LPCOLESTR name,DWORD compareFlags,IDiaEnumSymbols** ppResult) = 0; virtual HRESULT __stdcall get_targetSection(DWORD *ret) = 0; virtual HRESULT __stdcall get_targetOffset(DWORD *ret) = 0; virtual HRESULT __stdcall get_targetRelativeVirtualAddress(DWORD *ret) = 0; virtual HRESULT __stdcall get_targetVirtualAddress(ULONGLONG *ret) = 0; virtual HRESULT __stdcall get_machineType(DWORD *ret) = 0; virtual HRESULT __stdcall get_oemId(DWORD *ret) = 0; virtual HRESULT __stdcall get_oemSymbolId(DWORD *ret) = 0; virtual HRESULT __stdcall get_types(DWORD cTypes,DWORD *pcTypes,IDiaSymbol* types[]) = 0; virtual HRESULT __stdcall get_typeIds(DWORD cTypes,DWORD *pcTypeIds,DWORD typeIds[]) = 0; virtual HRESULT __stdcall get_objectPointerType(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_udtKind(DWORD *ret) = 0; virtual HRESULT __stdcall get_undecoratedNameEx(DWORD undecorateOptions,BSTR *name) = 0; }; class IDiaSectionContrib : public IUnknown { public: virtual HRESULT __stdcall get_compiland(IDiaSymbol **ret) = 0; virtual HRESULT __stdcall get_addressSection(DWORD *ret) = 0; virtual HRESULT __stdcall get_addressOffset(DWORD *ret) = 0; virtual HRESULT __stdcall get_relativeVirtualAddress(DWORD *ret) = 0; virtual HRESULT __stdcall get_virtualAddress(ULONGLONG *ret) = 0; virtual HRESULT __stdcall get_length(DWORD *ret) = 0; virtual HRESULT __stdcall get_notPaged(BOOL *ret) = 0; virtual HRESULT __stdcall get_code(BOOL *ret) = 0; virtual HRESULT __stdcall get_initializedData(BOOL *ret) = 0; virtual HRESULT __stdcall get_uninitializedData(BOOL *ret) = 0; virtual HRESULT __stdcall get_remove(BOOL *ret) = 0; virtual HRESULT __stdcall get_comdat(BOOL *ret) = 0; virtual HRESULT __stdcall get_discardable(BOOL *ret) = 0; virtual HRESULT __stdcall get_notCached(BOOL *ret) = 0; virtual HRESULT __stdcall get_share(BOOL *ret) = 0; virtual HRESULT __stdcall get_execute(BOOL *ret) = 0; virtual HRESULT __stdcall get_read(BOOL *ret) = 0; virtual HRESULT __stdcall get_write(BOOL *ret) = 0; virtual HRESULT __stdcall get_dataCrc(DWORD *ret) = 0; virtual HRESULT __stdcall get_relocationsaCrc(DWORD *ret) = 0; virtual HRESULT __stdcall get_compilandId(DWORD *ret) = 0; }; class IDiaTable : public IEnumUnknown { public: virtual HRESULT __stdcall get__NewEnum(IUnknown **ret) = 0; virtual HRESULT __stdcall get_name(BSTR *ret) = 0; virtual HRESULT __stdcall get_Count(LONG *ret) = 0; virtual HRESULT __stdcall Item(DWORD index,IUnknown **element) = 0; }; class DECLSPEC_UUID("e60afbee-502d-46ae-858f-8272a09bd707") DiaSource; class DECLSPEC_UUID("79f1bb5f-b66e-48e5-b6a9-1545c323ca3d") IDiaDataSource; /****************************************************************************/ struct PDBFileReader::SectionContrib { DWORD Section; DWORD Offset; DWORD Length; DWORD Compiland; BOOL CodeFlag; }; sU32 PDBFileReader::CompilandFromSectionOffset(sU32 sec,sU32 offs,sBool &codeFlag) { sInt l,r,x; l = 0; r = nContribs; while(l < r) { x = (l + r) / 2; const SectionContrib &cur = Contribs[x]; if(sec < cur.Section || sec == cur.Section && offs < cur.Offset) r = x; else if(sec > cur.Section || sec == cur.Section && offs >= cur.Offset + cur.Length) l = x+1; else // we got a winner { codeFlag = cur.CodeFlag; return cur.Compiland; } } // normally, this shouldn't happen! return 0; } // helpers static sChar *BStrToString(BSTR str,sChar *defString="") { if(!str) { sInt len = sGetStringLen(defString); sChar *buffer = new sChar[len+1]; sCopyString(buffer,defString,len+1); return buffer; } else { sInt len = SysStringLen(str); sChar *buffer = new sChar[len+1]; for(sInt i=0;i= 32 && str[i] < 128) ? str[i] : '?'; buffer[len] = 0; return buffer; } } static sInt GetBStr(BSTR str,sChar *defString,DebugInfo &to) { sChar *normalStr = BStrToString(str); sInt result = to.MakeString(normalStr); delete[] normalStr; return result; } void PDBFileReader::ProcessSymbol(IDiaSymbol *symbol,DebugInfo &to) { DWORD section,offset,rva; enum SymTagEnum tag; ULONGLONG length = 0; sU32 compilandId; IDiaSymbol *compiland = 0; BSTR name = 0, sourceName = 0; sBool codeFlag; symbol->get_symTag((DWORD *) &tag); symbol->get_relativeVirtualAddress(&rva); symbol->get_length(&length); symbol->get_addressSection(§ion); symbol->get_addressOffset(&offset); // is the previous symbol desperately looking for a length? we can help! if(DanglingLengthStart) { to.Symbols[to.Symbols.Count - 1].Size = rva - DanglingLengthStart; DanglingLengthStart = 0; } // get length from type for data if(tag == SymTagData) { IDiaSymbol *type; if(symbol->get_type(&type) == S_OK) { type->get_length(&length); type->Release(); } // if length still zero, just guess and take number of bytes between // this symbol and the next one. if(!length) DanglingLengthStart = rva; } compilandId = CompilandFromSectionOffset(section,offset,codeFlag); Session->symbolById(compilandId,&compiland); if(compiland) compiland->get_name(&sourceName); symbol->get_name(&name); // fill out structure sChar *nameStr = BStrToString(name,""); sChar *sourceStr = BStrToString(sourceName,""); if(tag == SymTagPublicSymbol) { length = 0; DanglingLengthStart = rva; } DISymbol *outSym = to.Symbols.Add(); outSym->Name.Index = outSym->MangledName.Index = to.MakeString(nameStr); outSym->FileNum = to.GetFileByName(sourceStr); outSym->VA = rva; outSym->Size = (sU32) length; outSym->Class = codeFlag ? DIC_CODE : DIC_DATA; outSym->NameSpNum = to.GetNameSpaceByName(nameStr); // clean up delete[] nameStr; delete[] sourceStr; if(compiland) compiland->Release(); if(sourceName) SysFreeString(sourceName); if(name) SysFreeString(name); } void PDBFileReader::ReadEverything(DebugInfo &to) { ULONG celt; Contribs = 0; nContribs = 0; DanglingLengthStart = 0; // read section table IDiaEnumTables *enumTables; if(Session->getEnumTables(&enumTables) == S_OK) { VARIANT vIndex; vIndex.vt = VT_BSTR; vIndex.bstrVal = SysAllocString(L"Sections"); IDiaTable *secTable; if(enumTables->Item(vIndex,&secTable) == S_OK) { LONG count; secTable->get_Count(&count); Contribs = new SectionContrib[count]; nContribs = 0; IDiaSectionContrib *item; while(SUCCEEDED(secTable->Next(1,(IUnknown **)&item,&celt)) && celt == 1) { SectionContrib &contrib = Contribs[nContribs++]; item->get_addressOffset(&contrib.Offset); item->get_addressSection(&contrib.Section); item->get_length(&contrib.Length); item->get_compilandId(&contrib.Compiland); item->get_execute(&contrib.CodeFlag); item->Release(); } secTable->Release(); } SysFreeString(vIndex.bstrVal); enumTables->Release(); } // enumerate symbols by (virtual) address IDiaEnumSymbolsByAddr *enumByAddr; if(SUCCEEDED(Session->getSymbolsByAddr(&enumByAddr))) { IDiaSymbol *symbol; // get first symbol to get first RVA (argh) if(SUCCEEDED(enumByAddr->symbolByAddr(1,0,&symbol))) { DWORD rva; if(symbol->get_relativeVirtualAddress(&rva) == S_OK) { symbol->Release(); // now, enumerate by rva. if(SUCCEEDED(enumByAddr->symbolByRVA(rva,&symbol))) { do { ProcessSymbol(symbol,to); symbol->Release(); if(FAILED(enumByAddr->Next(1,&symbol,&celt))) break; } while(celt == 1); } } else symbol->Release(); } enumByAddr->Release(); } // clean up delete[] Contribs; } /****************************************************************************/ sBool PDBFileReader::ReadDebugInfo(sChar *fileName,DebugInfo &to) { sBool readOk = sFALSE; if(FAILED(CoInitialize(0))) return sFALSE; IDiaDataSource *source = 0; CoCreateInstance(__uuidof(DiaSource),0,CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource),(void**) &source); if(source) { wchar_t wideFileName[260]; mbstowcs(wideFileName,fileName,260); if(SUCCEEDED(source->loadDataForExe(wideFileName,0,0))) { if(SUCCEEDED(source->openSession(&Session))) { ReadEverything(to); readOk = sTRUE; Session->Release(); } } source->Release(); } CoUninitialize(); return readOk; } /****************************************************************************/