1
0
Fork 0
x64dbg/src/dbg/pdbdiafile.cpp

1095 lines
30 KiB
C++

#include "_global.h"
#include <comutil.h>
#include <windows.h>
#include <thread>
#include <atomic>
#include <mutex>
#include <algorithm>
#include "msdia/dia2.h"
#include "msdia/cvConst.h"
#include "msdia/diacreate.h"
#include "pdbdiafile.h"
#include "stringutils.h"
#include "console.h"
#include "symbolundecorator.h"
//Taken from: https://msdn.microsoft.com/en-us/library/ms752876(v=vs.85).aspx
class FileStream : public IStream
{
FileStream(HANDLE hFile)
{
AddRef();
_hFile = hFile;
}
~FileStream()
{
if(_hFile != INVALID_HANDLE_VALUE)
{
::CloseHandle(_hFile);
}
}
public:
HRESULT static OpenFile(LPCWSTR pName, IStream** ppStream, bool fWrite)
{
HANDLE hFile = ::CreateFileW(pName, fWrite ? GENERIC_WRITE : GENERIC_READ, FILE_SHARE_READ,
NULL, fWrite ? CREATE_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if(hFile == INVALID_HANDLE_VALUE)
return HRESULT_FROM_WIN32(GetLastError());
*ppStream = new FileStream(hFile);
if(*ppStream == NULL)
CloseHandle(hFile);
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject)
{
if(iid == __uuidof(IUnknown)
|| iid == __uuidof(IStream)
|| iid == __uuidof(ISequentialStream))
{
*ppvObject = static_cast<IStream*>(this);
AddRef();
return S_OK;
}
else
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
return (ULONG)InterlockedIncrement(&_refcount);
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
ULONG res = (ULONG)InterlockedDecrement(&_refcount);
if(res == 0)
delete this;
return res;
}
// ISequentialStream Interface
public:
virtual HRESULT STDMETHODCALLTYPE Read(void* pv, ULONG cb, ULONG* pcbRead)
{
BOOL rc = ReadFile(_hFile, pv, cb, pcbRead, NULL);
return (rc) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
virtual HRESULT STDMETHODCALLTYPE Write(void const* pv, ULONG cb, ULONG* pcbWritten)
{
BOOL rc = WriteFile(_hFile, pv, cb, pcbWritten, NULL);
return rc ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
// IStream Interface
public:
virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE Commit(DWORD) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE Revert(void) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE Clone(IStream**) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)
{
DWORD dwMoveMethod;
switch(dwOrigin)
{
case STREAM_SEEK_SET:
dwMoveMethod = FILE_BEGIN;
break;
case STREAM_SEEK_CUR:
dwMoveMethod = FILE_CURRENT;
break;
case STREAM_SEEK_END:
dwMoveMethod = FILE_END;
break;
default:
return STG_E_INVALIDFUNCTION;
break;
}
if(SetFilePointerEx(_hFile, liDistanceToMove, (PLARGE_INTEGER)lpNewFilePointer,
dwMoveMethod) == 0)
return HRESULT_FROM_WIN32(GetLastError());
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)
{
if(GetFileSizeEx(_hFile, (PLARGE_INTEGER)&pStatstg->cbSize) == 0)
return HRESULT_FROM_WIN32(GetLastError());
return S_OK;
}
private:
HANDLE _hFile;
LONG _refcount = 0;
};
template<typename T>
class ScopedDiaType
{
private:
T* _sym;
public:
ScopedDiaType() : _sym(nullptr) {}
ScopedDiaType(T* sym) : _sym(sym) {}
~ScopedDiaType() { if(_sym != nullptr) _sym->Release(); }
T** ref() { return &_sym; }
T** operator&() { return ref(); }
T* operator->() { return _sym; }
operator T* () { return _sym; }
void Attach(T* sym) { _sym = sym; }
};
template<typename T>
using CComPtr = ScopedDiaType<T>;
PDBDiaFile::PDBDiaFile() :
m_stream(nullptr),
m_dataSource(nullptr),
m_session(nullptr)
{
}
PDBDiaFile::~PDBDiaFile()
{
if(isOpen())
close();
}
bool PDBDiaFile::initLibrary()
{
return SUCCEEDED(CoInitialize(nullptr));
}
bool PDBDiaFile::shutdownLibrary()
{
CoUninitialize();
return true;
}
bool PDBDiaFile::open(const char* file, uint64_t loadAddress, DiaValidationData_t* validationData)
{
return open(StringUtils::Utf8ToUtf16(file).c_str(), loadAddress, validationData);
}
bool PDBDiaFile::open(const wchar_t* file, uint64_t loadAddress, DiaValidationData_t* validationData)
{
if(!initLibrary())
return false;
if(isOpen())
{
#if 1 // Enable for validation purpose.
__debugbreak();
#endif
return false;
}
wchar_t fileExt[MAX_PATH] = { 0 };
wchar_t fileDir[MAX_PATH] = { 0 };
HRESULT hr = REGDB_E_CLASSNOTREG;
hr = NoRegCoCreate(L"msdia140.dll", __uuidof(DiaSource), __uuidof(IDiaDataSource), (LPVOID*)&m_dataSource);
if(testError(hr) || m_dataSource == nullptr)
{
hr = CoCreateInstance(__uuidof(DiaSource), NULL, CLSCTX_INPROC_SERVER, __uuidof(IDiaDataSource), (LPVOID*)&m_dataSource);
}
if(testError(hr) || m_dataSource == nullptr)
{
GuiSymbolLogAdd("Unable to initialize PDBDia Library.\n");
return false;
}
_wsplitpath_s(file, NULL, 0, fileDir, MAX_PATH, NULL, 0, fileExt, MAX_PATH);
if(_wcsicmp(fileExt, L".pdb") == 0)
{
hr = FileStream::OpenFile(file, &m_stream, false);
if(testError(hr))
{
GuiSymbolLogAdd("Unable to open PDB file.\n");
return false;
}
/*std::vector<unsigned char> pdbData;
if (!FileHelper::ReadAllData(StringUtils::Utf16ToUtf8(file), pdbData))
{
GuiSymbolLogAdd("Unable to open PDB file.\n");
return false;
}
m_stream = SHCreateMemStream(pdbData.data(), pdbData.size());*/
if(validationData != nullptr)
{
hr = m_dataSource->loadDataFromIStream(m_stream);
if(hr == E_PDB_FORMAT)
{
GuiSymbolLogAdd("PDB uses an obsolete format.\n");
return false;
}
}
else
{
hr = m_dataSource->loadDataFromIStream(m_stream);
}
}
else
{
// NOTE: Unsupported use with IStream.
hr = m_dataSource->loadDataForExe(file, fileDir, nullptr);
}
if(testError(hr))
{
if(hr != E_PDB_NOT_FOUND)
{
GuiSymbolLogAdd(StringUtils::sprintf("Unable to open PDB file - %08X\n", hr).c_str());
}
else
{
GuiSymbolLogAdd(StringUtils::sprintf("Unknown error - %08X\n", hr).c_str());
}
return false;
}
hr = m_dataSource->openSession(&m_session);
if(testError(hr) || m_session == nullptr)
{
GuiSymbolLogAdd(StringUtils::sprintf("Unable to create new PDBDia Session - %08X\n", hr).c_str());
return false;
}
if(validationData != nullptr)
{
IDiaSymbol* globalSym = nullptr;
hr = m_session->get_globalScope(&globalSym);
if(testError(hr))
{
//??
}
else
{
DWORD age = 0;
hr = globalSym->get_age(&age);
if(!testError(hr) && validationData->age != age)
{
globalSym->Release();
close();
GuiSymbolLogAdd(StringUtils::sprintf("Validation error: PDB age is not matching (expected: %u, actual: %u).\n", validationData->age, age).c_str());
return false;
}
if(validationData->signature != 0)
{
// PDB v2.0 ('NB10' ones) do not have a GUID and they use a signature and age
DWORD signature = 0;
hr = globalSym->get_signature(&signature);
if(!testError(hr) && validationData->signature != signature)
{
globalSym->Release();
close();
GuiSymbolLogAdd(StringUtils::sprintf("Validation error: PDB signature is not matching (expected: %08X, actual: %08X).\n",
signature, validationData->signature).c_str());
return false;
}
}
else
{
// v7.0 PDBs should be checked using (age+guid) only
GUID guid = { 0 };
hr = globalSym->get_guid(&guid);
if(!testError(hr) && memcmp(&guid, &validationData->guid, sizeof(GUID)) != 0)
{
globalSym->Release();
close();
auto guidStr = [](const GUID & guid) -> String
{
// https://stackoverflow.com/a/22848342/1806760
char guid_string[37]; // 32 hex chars + 4 hyphens + null terminator
sprintf_s(guid_string,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
guid.Data1, guid.Data2, guid.Data3,
guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5],
guid.Data4[6], guid.Data4[7]);
return guid_string;
};
GuiSymbolLogAdd(StringUtils::sprintf("Validation error: PDB guid is not matching (expected: %s, actual: %s).\n",
guidStr(validationData->guid).c_str(), guidStr(guid).c_str()).c_str());
return false;
}
}
}
globalSym->Release();
}
if(loadAddress != 0)
{
m_session->put_loadAddress(loadAddress);
}
return true;
}
bool PDBDiaFile::isOpen() const
{
return m_session != nullptr || m_dataSource != nullptr || m_stream != nullptr;
}
bool PDBDiaFile::close()
{
if(m_session)
{
auto refcount = m_session->Release();
if(refcount != 0)
dprintf("Memory leaks in IDiaSession (refcount: %u)\n", refcount);
m_session = nullptr;
}
if(m_dataSource)
{
auto refcount = m_dataSource->Release();
if(refcount != 0)
dprintf("Memory leaks in IDiaDataSource (refcount: %u)\n", refcount);
m_dataSource = nullptr;
}
if(m_stream)
{
auto refcount = m_stream->Release();
if(refcount != 0)
dprintf("Memory leaks in IStream (refcount: %u)\n", refcount);
m_stream = nullptr;
}
return true;
}
bool PDBDiaFile::testError(HRESULT hr)
{
if(FAILED(hr))
{
return true;
}
return false;
}
std::string PDBDiaFile::getSymbolNameString(IDiaSymbol* sym)
{
HRESULT hr;
BSTR str = nullptr;
std::string name;
hr = sym->get_name(&str);
if(hr != S_OK)
return name;
if(str != nullptr)
{
name = StringUtils::Utf16ToUtf8(str);
}
SysFreeString(str);
return name;
}
std::string PDBDiaFile::getSymbolUndecoratedNameString(IDiaSymbol* sym)
{
HRESULT hr;
BSTR str = nullptr;
std::string name;
std::string result;
hr = sym->get_undecoratedName(&str);
if(hr != S_OK)
{
return name;
}
if(str != nullptr)
{
name = StringUtils::Utf16ToUtf8(str);
}
result = name;
SysFreeString(str);
return result;
}
bool PDBDiaFile::enumerateLineNumbers(uint32_t rva, uint32_t size, std::vector<DiaLineInfo_t> & lines, std::map<DWORD, std::string> & files, const std::atomic<bool> & cancelled)
{
HRESULT hr;
DWORD lineNumber = 0;
DWORD relativeVirtualAddress = 0;
DWORD lineNumberEnd = 0;
CComPtr<IDiaEnumLineNumbers> lineNumbersEnum;
hr = m_session->findLinesByRVA(rva, size, &lineNumbersEnum);
if(!SUCCEEDED(hr))
return false;
LONG lineCount = 0;
hr = lineNumbersEnum->get_Count(&lineCount);
if(!SUCCEEDED(hr))
return false;
if(lineCount == 0)
return true;
lines.reserve(lines.size() + lineCount);
const ULONG bucket = 10000;
ULONG steps = lineCount / bucket + (lineCount % bucket != 0);
for(ULONG step = 0; step < steps; step++)
{
ULONG begin = step * bucket;
ULONG end = min((ULONG)lineCount, (step + 1) * bucket);
if(cancelled)
return false;
std::vector<IDiaLineNumber*> lineNumbers;
ULONG lineCountStep = end - begin;
lineNumbers.resize(lineCountStep);
ULONG fetched = 0;
hr = lineNumbersEnum->Next((ULONG)lineNumbers.size(), lineNumbers.data(), &fetched);
for(ULONG n = 0; n < fetched; n++)
{
if(cancelled)
{
for(ULONG m = n; m < fetched; m++)
lineNumbers[m]->Release();
return false;
}
CComPtr<IDiaLineNumber> lineNumberInfo;
lineNumberInfo.Attach(lineNumbers[n]);
DWORD sourceFileId = 0;
hr = lineNumberInfo->get_sourceFileId(&sourceFileId);
if(!SUCCEEDED(hr))
continue;
if(!files.count(sourceFileId))
{
CComPtr<IDiaSourceFile> sourceFile;
hr = lineNumberInfo->get_sourceFile(&sourceFile);
if(!SUCCEEDED(hr))
continue;
BSTR fileName = nullptr;
hr = sourceFile->get_fileName(&fileName);
if(!SUCCEEDED(hr))
continue;
files.insert({ sourceFileId, StringUtils::Utf16ToUtf8(fileName) });
SysFreeString(fileName);
}
DiaLineInfo_t lineInfo;
lineInfo.sourceFileId = sourceFileId;
hr = lineNumberInfo->get_lineNumber(&lineInfo.lineNumber);
if(!SUCCEEDED(hr))
continue;
hr = lineNumberInfo->get_relativeVirtualAddress(&lineInfo.rva);
if(!SUCCEEDED(hr))
continue;
lines.push_back(lineInfo);
}
}
return true;
}
uint32_t getSymbolId(IDiaSymbol* sym)
{
DWORD id;
sym->get_symIndexId(&id);
return id;
}
bool PDBDiaFile::enumerateLexicalHierarchy(const Query_t & query)
{
CComPtr<IDiaSymbol> globalScope;
IDiaSymbol* symbol = nullptr;
ULONG celt = 0;
HRESULT hr;
DiaSymbol_t symbolInfo;
bool res = true;
hr = m_session->get_globalScope(&globalScope);
if(hr != S_OK)
return false;
InternalQueryContext_t context;
context.callback = query.callback;
context.collectSize = query.collectSize;
context.collectUndecoratedNames = query.collectUndecoratedNames;
uint32_t scopeId = getSymbolId(globalScope);
context.visited.insert(scopeId);
// Enumerate publics.
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = globalScope->findChildren(SymTagPublicSymbol, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
if(convertSymbolInfo(symbol, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
// Enumerate global functions.
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = globalScope->findChildren(SymTagFunction, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
// Enumerate global data.
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = globalScope->findChildren(SymTagData, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
// Enumerate compilands.
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = globalScope->findChildren(SymTagCompiland, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
if(!enumerateCompilandScope(sym, context))
{
return false;
}
}
}
}
return true;
}
bool PDBDiaFile::findSymbolRVA(uint64_t address, DiaSymbol_t & sym, DiaSymbolType symType /*= DiaSymbolType::ANY*/)
{
if(m_session == nullptr || m_dataSource == nullptr)
return false;
IDiaEnumSymbols* enumSymbols = nullptr;
IDiaSymbol* symbol = nullptr;
ULONG celt = 0;
HRESULT hr;
enum SymTagEnum tag = SymTagNull;
switch(symType)
{
case DiaSymbolType::BLOCK:
tag = SymTagBlock;
break;
case DiaSymbolType::FUNCTION:
tag = SymTagFunction;
break;
case DiaSymbolType::LABEL:
tag = SymTagLabel;
break;
case DiaSymbolType::PUBLIC:
tag = SymTagPublicSymbol;
break;
}
long disp = 0;
hr = m_session->findSymbolByRVAEx((DWORD)address, tag, &symbol, &disp);
if(hr != S_OK)
return false;
CComPtr<IDiaSymbol> scopedSym(symbol);
sym.disp = disp;
InternalQueryContext_t context;
context.collectSize = true;
context.collectUndecoratedNames = true;
if(!convertSymbolInfo(scopedSym, sym, context))
return false;
return true;
}
bool PDBDiaFile::enumerateCompilandScope(IDiaSymbol* compiland, InternalQueryContext_t & context)
{
IDiaSymbol* symbol = nullptr;
ULONG celt = 0;
HRESULT hr;
DWORD symTagType;
DiaSymbol_t symbolInfo;
uint32_t symId = getSymbolId(compiland);
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = compiland->findChildren(SymTagFunction, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(!processFunctionSymbol(sym, context))
{
return false;
}
}
}
}
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = compiland->findChildren(SymTagData, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = compiland->findChildren(SymTagBlock, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = compiland->findChildren(SymTagLabel, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
return true;
}
bool PDBDiaFile::processFunctionSymbol(IDiaSymbol* functionSym, InternalQueryContext_t & context)
{
IDiaSymbol* symbol = nullptr;
ULONG celt = 0;
HRESULT hr;
DWORD symTagType;
uint32_t symId = getSymbolId(functionSym);
if(context.visited.find(symId) != context.visited.end())
{
GuiSymbolLogAdd("Dupe\n");
return true;
}
context.visited.insert(symId);
DiaSymbol_t symbolInfo;
if(convertSymbolInfo(functionSym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
return false;
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = functionSym->findChildren(SymTagData, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
LocationType locType;
sym->get_locationType((DWORD*)&locType);
if(hr == S_OK && locType == LocIsStatic)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = functionSym->findChildren(SymTagBlock, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
{
CComPtr<IDiaEnumSymbols> enumSymbols;
hr = functionSym->findChildren(SymTagLabel, nullptr, nsNone, &enumSymbols);
if(hr == S_OK)
{
while((hr = enumSymbols->Next(1, &symbol, &celt)) == S_OK && celt == 1)
{
CComPtr<IDiaSymbol> sym(symbol);
hr = sym->get_symTag(&symTagType);
if(hr == S_OK)
{
if(convertSymbolInfo(sym, symbolInfo, context))
{
if(!context.callback(symbolInfo))
{
return false;
}
}
}
}
}
}
return true;
}
bool PDBDiaFile::resolveSymbolSize(IDiaSymbol* symbol, uint64_t & size, uint32_t symTag)
{
bool res = false;
HRESULT hr;
uint64_t tempSize = -1;
if(symTag == SymTagData)
{
CComPtr<IDiaSymbol> symType;
hr = symbol->get_type(&symType);
if(hr == S_OK && symType != nullptr)
{
DWORD symTagType = 0;
hr = symType->get_symTag(&symTagType);
switch(symTagType)
{
case SymTagFunctionType:
{
hr = symbol->get_length(&tempSize);
if(hr == S_OK)
{
size = tempSize;
}
else
res = false;
}
break;
case SymTagPointerType:
case SymTagArrayType:
case SymTagUDT:
{
hr = symType->get_length(&tempSize);
if(hr == S_OK)
size = tempSize;
else
res = false;
}
break;
case SymTagNull:
{
hr = symType->get_length(&tempSize);
if(hr == S_OK)
{
size = tempSize;
}
else
{
hr = symbol->get_length(&tempSize);
if(hr == S_OK)
size = tempSize;
else
res = false;
}
}
break;
default:
{
// Native type.
hr = symType->get_length(&tempSize);
if(hr == S_OK)
size = tempSize;
else
res = false;
}
break;
}
}
// One last attempt.
if(res == false || size == 0 || size == -1)
{
hr = symbol->get_length(&tempSize);
if(hr == S_OK)
{
size = tempSize;
res = true;
}
else
res = false;
}
}
else if(symTag == SymTagFunction ||
symTag == SymTagBlock)
{
hr = symbol->get_length(&tempSize);
if(hr == S_OK)
{
size = tempSize;
}
else
res = false;
}
return res;
}
bool PDBDiaFile::convertSymbolInfo(IDiaSymbol* symbol, DiaSymbol_t & symbolInfo, InternalQueryContext_t & context)
{
HRESULT hr;
DWORD symTagType;
// Default all values.
symbolInfo.reachable = DiaReachableType::UNKNOWN;
symbolInfo.returnable = DiaReturnableType::UNKNOWN;
symbolInfo.convention = DiaCallingConvention::UNKNOWN;
symbolInfo.size = -1;
symbolInfo.offset = -1;
symbolInfo.segment = -1;
symbolInfo.disp = 0;
symbolInfo.virtualAddress = -1;
symbolInfo.perfectSize = false;
symbolInfo.publicSymbol = false;
hr = symbol->get_symTag(&symTagType);
if(hr != S_OK)
return false;
symbolInfo.name = getSymbolNameString(symbol);
if(context.collectUndecoratedNames && !symbolInfo.name.empty() && (symbolInfo.name.at(0) == '?' || symbolInfo.name.at(0) == '_' || symbolInfo.name.at(0) == '@'))
{
undecorateName(symbolInfo.name, symbolInfo.undecoratedName);
}
else
{
symbolInfo.undecoratedName = "";
}
hr = symbol->get_addressSection((DWORD*)&symbolInfo.segment);
if(hr != S_OK)
{
return false;
}
hr = symbol->get_addressOffset((DWORD*)&symbolInfo.offset);
if(hr != S_OK)
{
return false;
}
hr = symbol->get_virtualAddress(&symbolInfo.virtualAddress);
if(hr != S_OK)
{
// NOTE: At this point we could still lookup the address over the executable.
return false;
}
if(symbolInfo.virtualAddress == symbolInfo.offset)
{
return false;
}
symbolInfo.size = -1;
if(context.collectSize)
{
if(!resolveSymbolSize(symbol, symbolInfo.size, symTagType) || symbolInfo.size == 0)
{
symbolInfo.size = -1;
}
}
switch(symTagType)
{
case SymTagPublicSymbol:
symbolInfo.type = DiaSymbolType::PUBLIC;
symbolInfo.publicSymbol = true;
break;
case SymTagFunction:
symbolInfo.type = DiaSymbolType::FUNCTION;
break;
case SymTagData:
symbolInfo.type = DiaSymbolType::DATA;
break;
case SymTagLabel:
symbolInfo.type = DiaSymbolType::LABEL;
break;
case SymTagBlock:
symbolInfo.type = DiaSymbolType::BLOCK;
break;
}
return true;
}