You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3943 lines
94 KiB
3943 lines
94 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dia.c
|
|
|
|
Abstract:
|
|
|
|
These routines call VC's new DIA symbol handler.
|
|
|
|
Author:
|
|
|
|
Pat Styles (patst) 26-May-2000
|
|
|
|
Environment:
|
|
|
|
User Mode
|
|
|
|
--*/
|
|
#define DIA_LIBRARY 1
|
|
|
|
#include "private.h"
|
|
#include "symbols.h"
|
|
#include "globals.h"
|
|
#include "dia2.h"
|
|
#include "diacreate_int.h"
|
|
#include "pdb.h"
|
|
#include <atlbase.h>
|
|
|
|
typedef struct {
|
|
CComPtr<IDiaDataSource> source;
|
|
CComPtr<IDiaSession> session;
|
|
CComPtr<IDiaSymbol> scope;
|
|
CComPtr<IDiaSourceFile> srcfile;
|
|
CComPtr<IDiaEnumFrameData> framedata;
|
|
#ifdef BBTFIX
|
|
CComPtr<IDiaAddressMap> addrmap;
|
|
#endif
|
|
} DIA, *PDIA;
|
|
|
|
extern HRESULT STDMETHODCALLTYPE DiaCoCreate(
|
|
REFCLSID rclsid,
|
|
REFIID riid,
|
|
void **ppv);
|
|
|
|
extern HRESULT STDMETHODCALLTYPE NoOleCoCreate(REFCLSID rclsid,
|
|
REFIID riid,
|
|
void **ppv);
|
|
|
|
#define freeString LocalFree
|
|
|
|
// used by diaLocatePdb
|
|
|
|
enum {
|
|
ipNone = 0,
|
|
ipFirst,
|
|
ipLast
|
|
};
|
|
|
|
|
|
BOOL diaInit()
|
|
{
|
|
#ifdef COMDIA
|
|
HRESULT hr;
|
|
|
|
if (!g.fCoInit)
|
|
hr = CoInitialize(NULL);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
g.fCoInit = true;
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
|
|
void diaCleanup()
|
|
{
|
|
#ifdef COMDIA
|
|
HRESULT hr;
|
|
|
|
if (!g.cProcessList && g.fCoInit)
|
|
CoUninitialize();
|
|
g.fCoInit = false;
|
|
#endif
|
|
}
|
|
|
|
|
|
__inline
|
|
HRESULT
|
|
SetDiaError(
|
|
HRESULT ccode,
|
|
HRESULT ncode
|
|
)
|
|
{
|
|
if (ncode == EC_OK)
|
|
return ncode;
|
|
|
|
if (ccode != (HRESULT)EC_NOT_FOUND)
|
|
return ccode;
|
|
|
|
return ncode;
|
|
}
|
|
|
|
|
|
__inline
|
|
BOOL
|
|
ValidSig(
|
|
DWORD sig,
|
|
GUID *guid
|
|
)
|
|
{
|
|
if (ValidGuid(guid))
|
|
return true;
|
|
|
|
if (sig)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
typedef struct _DIAERROR {
|
|
HRESULT hr;
|
|
char *text;
|
|
} DIAERROR, *PDIAERROR;
|
|
|
|
|
|
char *
|
|
diaErrorText(
|
|
HRESULT hr
|
|
)
|
|
{
|
|
#define ERROR_MAX 24
|
|
|
|
static const DIAERROR error[ERROR_MAX] =
|
|
{
|
|
{E_PDB_OK, "OK"},
|
|
{E_PDB_USAGE, "invalid parameters"},
|
|
{E_PDB_OUT_OF_MEMORY, "out of memory"},
|
|
{E_PDB_FILE_SYSTEM, "drive not ready"},
|
|
{E_PDB_NOT_FOUND, "file not found"},
|
|
{E_PDB_INVALID_SIG, "mismatched pdb"},
|
|
{E_PDB_INVALID_AGE, "mismatched pdb"},
|
|
{E_PDB_PRECOMP_REQUIRED, "E_PDB_PRECOMP_REQUIRED"},
|
|
{E_PDB_OUT_OF_TI, "E_PDB_OUT_OF_TI"},
|
|
{E_PDB_NOT_IMPLEMENTED, "E_PDB_NOT_IMPLEMENTED"},
|
|
{E_PDB_V1_PDB, "E_PDB_V1_PDB"},
|
|
{E_PDB_FORMAT, "file system or network error reading pdb"},
|
|
{E_PDB_LIMIT, "E_PDB_LIMIT"},
|
|
{E_PDB_CORRUPT, "E_PDB_CORRUPT"},
|
|
{E_PDB_TI16, "E_PDB_TI16"},
|
|
{E_PDB_ACCESS_DENIED, "E_PDB_ACCESS_DENIED"},
|
|
{E_PDB_ILLEGAL_TYPE_EDIT, "E_PDB_ILLEGAL_TYPE_EDIT"},
|
|
{E_PDB_INVALID_EXECUTABLE, "invalid executable image"},
|
|
{E_PDB_DBG_NOT_FOUND, "dbg file not found"},
|
|
{E_PDB_NO_DEBUG_INFO, "pdb is stripped of cv info"},
|
|
{E_PDB_INVALID_EXE_TIMESTAMP, "image has invalid timestamp"},
|
|
{E_PDB_RESERVED, "E_PDB_RESERVED"},
|
|
{E_PDB_DEBUG_INFO_NOT_IN_PDB, "pdb has no symbols"},
|
|
{E_PDB_MAX, "pdb error 0x%x"}
|
|
};
|
|
|
|
static char sz[50];
|
|
|
|
DWORD i;
|
|
|
|
for (i = 0; i < ERROR_MAX; i++) {
|
|
if (hr == error[i].hr)
|
|
return error[i].text;
|
|
}
|
|
|
|
PrintString(sz, DIMA(sz), "dia error 0x%x", hr);
|
|
return sz;
|
|
}
|
|
|
|
|
|
void
|
|
FreeDiaVariant(
|
|
VARIANT *var
|
|
)
|
|
{
|
|
if (!var || !var->bstrVal)
|
|
return;
|
|
|
|
#ifdef COMDIA
|
|
SysFreeString(var->bstrVal);
|
|
#else
|
|
LocalFree(var->bstrVal);
|
|
#endif
|
|
var->vt = VT_EMPTY;
|
|
}
|
|
|
|
extern DWORD DIA_VERSION;
|
|
|
|
DWORD
|
|
diaVersion(
|
|
VOID
|
|
)
|
|
{
|
|
return DIA_VERSION;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetPdbInfo(
|
|
PIMGHLP_DEBUG_DATA idd
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
DWORD celt;
|
|
int i;
|
|
enum SymTagEnum symtag[2] = {SymTagData, SymTagFunction};
|
|
enum SymTagEnum typetag[2] = {SymTagTypedef, SymTagUDT};
|
|
|
|
assert(idd);
|
|
|
|
// get interface
|
|
|
|
pdia = (PDIA)idd->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
CComPtr<IDiaSymbol> idiaGlobals;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaGlobals);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// get the pdb age and sig
|
|
|
|
hr = idiaGlobals->get_guid(&idd->pdbdataGuid);
|
|
if (hr != S_OK)
|
|
return false;
|
|
if (GuidIsDword(&idd->pdbdataGuid)) {
|
|
idd->pdbdataSig = idd->pdbdataGuid.Data1;
|
|
idd->pdbdataGuid.Data1 = 0;
|
|
}
|
|
hr = idiaGlobals->get_age(&idd->pdbdataAge);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// any line numbers?
|
|
|
|
CComPtr<IDiaEnumSourceFiles> idiaSrcFiles;
|
|
CComPtr<IDiaSourceFile> idiaSrcFile;
|
|
|
|
hr = pdia->session->findFile(NULL, NULL, 0, &idiaSrcFiles);
|
|
if (hr == S_OK) {
|
|
hr = idiaSrcFiles->Next(1, &idiaSrcFile, &celt);
|
|
if (hr == S_OK && celt > 0)
|
|
idd->fLines = true;
|
|
}
|
|
|
|
// any symbols ?
|
|
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
CComPtr< IDiaEnumSymbols > idiaSymbols;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
hr = idiaGlobals->findChildren(symtag[i], NULL, 0, &idiaSymbols);
|
|
if (hr == S_OK) {
|
|
hr = idiaSymbols->Next(1, &idiaSymbol, &celt);
|
|
if (hr == S_OK && celt > 0) {
|
|
idd->fSymbols = true;
|
|
break;
|
|
}
|
|
}
|
|
idiaSymbols = NULL;
|
|
idiaSymbol = NULL;
|
|
}
|
|
|
|
// any type info?
|
|
|
|
idiaSymbols = NULL;
|
|
idiaSymbol = NULL;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
hr = idiaGlobals->findChildren(typetag[i], NULL, 0, &idiaSymbols);
|
|
if (hr == S_OK) {
|
|
hr = idiaSymbols->Next(1, &idiaSymbol, &celt);
|
|
if (hr == S_OK && celt > 0) {
|
|
idd->fTypes = true;
|
|
break;
|
|
}
|
|
}
|
|
idiaSymbols = NULL;
|
|
idiaSymbol = NULL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
diaOpenPdb(
|
|
PIMGHLP_DEBUG_DATA idd,
|
|
PSTR szPDB,
|
|
GUID *PdbGUID,
|
|
DWORD PdbSignature,
|
|
DWORD PdbAge,
|
|
BOOL MatchAnything
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
EC hrcode = E_PDB_NOT_FOUND;
|
|
PDIA pdia;
|
|
WCHAR wszPDB[_MAX_PATH + 1];
|
|
|
|
pdia = (PDIA)idd->dia;
|
|
if (!pdia)
|
|
return EC_NO_DEBUG_INFO;
|
|
|
|
ansi2wcs(szPDB, wszPDB, DIMA(wszPDB));
|
|
if (!ValidSig(PdbSignature, PdbGUID))
|
|
hr = pdia->source->loadDataFromPdb(wszPDB);
|
|
else
|
|
hr = pdia->source->loadAndValidateDataFromPdb(wszPDB,
|
|
ValidGuid(PdbGUID) ? PdbGUID : NULL,
|
|
PdbSignature,
|
|
PdbAge);
|
|
hrcode = SetDiaError(hrcode, hr);
|
|
if (hr == S_OK) {
|
|
if (!PdbSignature && !ValidGuid(PdbGUID))
|
|
idd->fPdbUnmatched = true;
|
|
} else {
|
|
pprint(idd->pe, "%s - %s\n", szPDB, diaErrorText(hr));
|
|
if (hr == E_PDB_INVALID_SIG || hr == E_PDB_INVALID_AGE) {
|
|
if (!ValidSig(PdbSignature, PdbGUID)) {
|
|
hr = pdia->source->loadDataFromPdb(wszPDB);
|
|
} else if (!*idd->FoundPdb)
|
|
CopyStrArray(idd->FoundPdb, szPDB);
|
|
} else if (hr == E_PDB_NOT_FOUND) {
|
|
if (!(g.LastSymLoadError & SYMLOAD_PDBERRORMASK)) {
|
|
g.LastSymLoadError = SYMLOAD_PDBNOTFOUND;
|
|
}
|
|
} else {
|
|
g.LastSymLoadError = (hr << 8) & SYMLOAD_PDBERRORMASK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CheckDirForPdbs(
|
|
PIMGHLP_DEBUG_DATA idd,
|
|
PSTR path,
|
|
GUID *PdbGUID,
|
|
DWORD PdbSignature,
|
|
DWORD PdbAge
|
|
)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE hf;
|
|
HRESULT hr;
|
|
char drive[_MAX_DRIVE + 1];
|
|
char dir[_MAX_DIR + 1];
|
|
char fname[_MAX_FNAME + 1];
|
|
char sfname[_MAX_FNAME + 1];
|
|
char ext[_MAX_EXT + 1];
|
|
char spath[MAX_PATH + 1];
|
|
|
|
if (!*path)
|
|
return E_PDB_NOT_FOUND;
|
|
|
|
_splitpath(path, drive, dir, fname, ext);
|
|
ShortNodeName(fname, sfname, DIMA(sfname));
|
|
|
|
// now search the tree
|
|
|
|
PrintString(spath, DIMA(spath), "%s%s%s%s\\*", drive, dir, sfname, ext);
|
|
|
|
ZeroMemory(&fd, sizeof(fd));
|
|
hf = FindFirstFile(spath, &fd);
|
|
if (hf == INVALID_HANDLE_VALUE)
|
|
return E_PDB_NOT_FOUND;
|
|
|
|
do {
|
|
if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))
|
|
continue;
|
|
PrintString(spath, DIMA(spath), "%s%s%s%s\\%s", drive, dir, sfname, ext, fd.cFileName);
|
|
if (isdir(spath)) {
|
|
EnsureTrailingBackslash(spath);
|
|
CatStrArray(spath, sfname);
|
|
CatStrArray(spath, ext);
|
|
} else if (!IsPdb(spath))
|
|
continue;
|
|
hr = diaOpenPdb(idd, spath, PdbGUID, PdbSignature, PdbAge, false);
|
|
if (hr == S_OK) {
|
|
CopyString(path, spath, MAX_PATH + 1);
|
|
return hr;
|
|
}
|
|
} while (FindNextFile(hf, &fd));
|
|
|
|
// If there is no match, but a file exists in the symbol subdir with
|
|
// a matching name, make sure that is what will be picked.
|
|
|
|
PrintString(spath, DIMA(spath), "%s%s%s%s\\%s%s", drive, dir, sfname, ext, sfname, ext);
|
|
if (fileexists(spath))
|
|
CopyStrArray(idd->FoundPdb, spath);
|
|
|
|
return E_PDB_NOT_FOUND;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
diaLocatePdb(
|
|
PIMGHLP_DEBUG_DATA idd,
|
|
PSTR szPDB,
|
|
GUID *PdbGUID,
|
|
DWORD PdbSignature,
|
|
DWORD PdbAge,
|
|
char *szImageExt
|
|
)
|
|
{
|
|
DWORD pass;
|
|
EC hrcode = E_PDB_NOT_FOUND;
|
|
GUID guid;
|
|
HRESULT hr = E_PDB_NOT_FOUND;
|
|
|
|
char pdb[MAX_PATH + 1];
|
|
char drive[6];
|
|
char path[MAX_PATH + 1];
|
|
char module[MAX_PATH + 1];
|
|
char modbuf[MAX_PATH + 1];
|
|
char name[MAX_PATH + 1];
|
|
char ext[_MAX_EXT + 1];
|
|
char *next;
|
|
DWORD attrib;
|
|
DWORD err;
|
|
BOOL ssrv = true;
|
|
|
|
#ifdef DEBUG
|
|
if (traceSubName(szPDB)) // for setting debug breakpoints from DBGHELP_TOKEN
|
|
dtrace("diaLocatePdb(%s)\n", szPDB);
|
|
#endif
|
|
|
|
if (!PdbSignature
|
|
&& !IsPdb(idd->ImageFilePath)
|
|
&& !ValidGuid(PdbGUID)
|
|
&& option(SYMOPT_EXACT_SYMBOLS))
|
|
{
|
|
g.LastSymLoadError = SYMLOAD_PDBUNMATCHED;
|
|
return E_PDB_INVALID_SIG;
|
|
}
|
|
|
|
if (!idd->dia)
|
|
return EC_NO_DEBUG_INFO;
|
|
|
|
// If the image file name is a pdb, then just try to open it.
|
|
// Don't attempt any searching.
|
|
|
|
if (IsPdb(idd->ImageFilePath)) {
|
|
CopyStrArray(pdb, idd->ImageFilePath);
|
|
hr = diaOpenPdb(idd, pdb, PdbGUID, PdbSignature, PdbAge, false);
|
|
if (hr == S_OK) {
|
|
idd->PdbSrc = srcImagePath;
|
|
goto done;
|
|
}
|
|
return E_PDB_NOT_FOUND;
|
|
}
|
|
|
|
// Set up indexes for symbol server
|
|
|
|
ZeroMemory(&guid, sizeof(GUID));
|
|
if (PdbSignature)
|
|
guid.Data1 = PdbSignature;
|
|
else if (PdbGUID)
|
|
memcpy(&guid, PdbGUID, sizeof(GUID));
|
|
|
|
// get name of pdb
|
|
|
|
_splitpath(szPDB, NULL, NULL, module, ext);
|
|
PrintString(name, DIMA(name), "%s%s", module, ".pdb");
|
|
|
|
// SymbolPath is a semicolon delimited path (reference path first)
|
|
|
|
next = TokenFromSymbolPath(idd->SymbolPath, path, MAX_PATH + 1);
|
|
while (*path) {
|
|
|
|
for (pass = 0; pass < 3; pass++) {
|
|
|
|
if (symsrvPath(path)) {
|
|
if (pass || !ssrv)
|
|
break;
|
|
*pdb = 0;
|
|
idd->PdbSrc = srcSymSrv;
|
|
err = symsrvGetFile(idd->pe,
|
|
path,
|
|
name,
|
|
&guid,
|
|
PdbAge,
|
|
0,
|
|
pdb);
|
|
if (err == ERROR_NO_DATA)
|
|
ssrv = false;
|
|
|
|
} else {
|
|
if (pass && !*szImageExt)
|
|
break;
|
|
idd->PdbSrc = srcSearchPath;
|
|
if (!CreateSymbolPath(pass, path, szImageExt, module, ext, pdb, DIMA(pdb))) {
|
|
hr = E_PDB_NOT_FOUND;
|
|
goto done;
|
|
}
|
|
if (!pass) {
|
|
hr = CheckDirForPdbs(idd, pdb, PdbGUID, PdbSignature, PdbAge);
|
|
if (hr == S_OK)
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (*pdb) {
|
|
hr = diaOpenPdb(idd, pdb, PdbGUID, PdbSignature, PdbAge, false);
|
|
hrcode = SetDiaError(hrcode, hr);
|
|
if (hr == S_OK)
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
next = TokenFromSymbolPath(next, path, MAX_PATH + 1);
|
|
}
|
|
|
|
// try the same path as the image
|
|
|
|
if (idd->ImageFileHandle && *idd->ImageFilePath) {
|
|
_splitpath(idd->ImageFilePath, drive, path, NULL, NULL);
|
|
PrintString(pdb, DIMA(pdb), "%s%s%s", drive, path, name);
|
|
hr = diaOpenPdb(idd, pdb, PdbGUID, PdbSignature, PdbAge, false);
|
|
if (hr == S_OK)
|
|
idd->PdbSrc = srcImagePath;
|
|
}
|
|
|
|
// try the CV Record
|
|
|
|
if (hr != S_OK && strcmp(pdb, szPDB) && !option(SYMOPT_IGNORE_CVREC)) {
|
|
CopyStrArray(pdb, szPDB);
|
|
hr = diaOpenPdb(idd, pdb, PdbGUID, PdbSignature, PdbAge, false);
|
|
if (hr == S_OK)
|
|
idd->PdbSrc = srcCVRec;
|
|
}
|
|
|
|
// try mismatches
|
|
|
|
if (hr != S_OK && *idd->FoundPdb) {
|
|
if (option(SYMOPT_LOAD_ANYTHING)) {
|
|
CopyStrArray(pdb, idd->FoundPdb);
|
|
hr = diaOpenPdb(idd, pdb, NULL, 0, 0, true);
|
|
if (hr == S_OK)
|
|
idd->PdbSrc = srcSearchPath;
|
|
}
|
|
idd->LoadInfo &= DSLFLAG_MISMATCHED_PDB;
|
|
pprint(idd->pe, "%s mismatched pdb for %s\n",
|
|
hr == S_OK ? "Loaded" : "Couldn't load",
|
|
*idd->ImageFilePath ? idd->ImageFilePath : name);
|
|
}
|
|
|
|
done:
|
|
|
|
if (hr == S_OK) {
|
|
// Store the name of the PDB we actually opened for later reference.
|
|
strcpy(szPDB, pdb); // SECURITY: Don't know size of target buffer.
|
|
SetLastError(NO_ERROR);
|
|
g.LastSymLoadError = SYMLOAD_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaReadStream(
|
|
PMODULE_ENTRY mi,
|
|
char *stream,
|
|
PBYTE *buf,
|
|
DWORD *size
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
PDB *pdb;
|
|
HRESULT hr;
|
|
BOOL rc;
|
|
LONG cb;
|
|
Stream *pstream;
|
|
LONG count = 0;
|
|
|
|
assert (mi && stream && *stream && buf && size);
|
|
*size = 0;
|
|
*buf = 0;
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
hr = GetRawPdbPtrForDataSource(pdia->source, &pdb);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
rc = PDBOpenStream(pdb, stream, &pstream);
|
|
if (!rc)
|
|
return false;
|
|
|
|
*size = StreamQueryCb(pstream);
|
|
if (!*size)
|
|
return false;
|
|
|
|
*buf = (PBYTE)MemAlloc(*size + 1);
|
|
if (!*buf)
|
|
return false;
|
|
|
|
cb = *size;
|
|
rc = StreamRead(pstream, 0, *buf, &cb);
|
|
if (!rc)
|
|
goto error;
|
|
if (cb != *size)
|
|
goto error;
|
|
|
|
return true;
|
|
|
|
error:
|
|
MemFree(*buf);
|
|
*buf = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD
|
|
diaReadDebugStream(
|
|
PVOID dia,
|
|
char *stream,
|
|
PBYTE *buf,
|
|
DWORD *size
|
|
)
|
|
{
|
|
DWORD celt;
|
|
LONG count;
|
|
DWORD cb;
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
PDIA pdia;
|
|
WCHAR wstream[1000];
|
|
|
|
CComPtr< IDiaEnumDebugStreams > idiaStreams;
|
|
CComPtr< IDiaEnumDebugStreamData > idiaStream;
|
|
|
|
assert (dia && stream && *stream && buf);
|
|
|
|
pdia = (PDIA)dia;
|
|
hr = pdia->session->getEnumDebugStreams(&idiaStreams);
|
|
if (hr != S_OK)
|
|
return 0;
|
|
|
|
if (!ansi2wcs(stream, wstream, 1000))
|
|
return 0;
|
|
|
|
var.vt = VT_BSTR;
|
|
var.bstrVal = wstream;
|
|
hr = idiaStreams->Item(var, &idiaStream);
|
|
if (hr != S_OK)
|
|
return 0;
|
|
|
|
hr = idiaStream->get_Count(&count);
|
|
if (hr != S_OK)
|
|
return 0;
|
|
if (count < 1)
|
|
return 0;
|
|
|
|
hr = idiaStream->Next(count, 0, &cb, NULL, &celt);
|
|
if (hr != S_OK)
|
|
return 0;
|
|
if (cb < 1)
|
|
return 0;
|
|
|
|
*buf = (PBYTE)MemAlloc(cb);
|
|
if (!*buf)
|
|
return 0;
|
|
|
|
hr = idiaStream->Next(count, cb, &cb, *buf, &celt);
|
|
if (hr != S_OK) {
|
|
MemFree(*buf);
|
|
*buf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
*size = cb;
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetOmaps(
|
|
PIMGHLP_DEBUG_DATA idd
|
|
)
|
|
{
|
|
DWORD celt;
|
|
LONG count;
|
|
DWORD cb;
|
|
PBYTE tbuf = NULL;
|
|
PBYTE fbuf = NULL;
|
|
HRESULT hr;
|
|
VARIANT var;
|
|
|
|
count = diaReadDebugStream(idd->dia, "OMAPTO", &tbuf, &cb);
|
|
if (count < 1)
|
|
return false;
|
|
|
|
idd->cOmapTo = count;
|
|
idd->pOmapTo = (POMAP)tbuf;
|
|
idd->fOmapToMapped = false;
|
|
|
|
count = diaReadDebugStream(idd->dia, "OMAPFROM", &fbuf, &cb);
|
|
if (count < 1)
|
|
return false;
|
|
|
|
idd->cOmapFrom = count;
|
|
idd->pOmapFrom = (POMAP)fbuf;
|
|
idd->fOmapFromMapped = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetFPOTable(
|
|
PIMGHLP_DEBUG_DATA idd
|
|
)
|
|
{
|
|
LONG count;
|
|
PBYTE buf;
|
|
DWORD cb;
|
|
|
|
count = diaReadDebugStream(idd->dia, "FPO", &buf, &cb);
|
|
if (count < 1)
|
|
return false;
|
|
|
|
idd->cFpo = count;
|
|
idd->pFpo = buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetPData(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
LONG count;
|
|
PBYTE buf;
|
|
DWORD cb;
|
|
|
|
count = diaReadDebugStream(mi->dia, "PDATA", &buf, &cb);
|
|
if (count < 1)
|
|
return false;
|
|
|
|
mi->dsExceptions = dsDia;
|
|
mi->cPData = count;
|
|
mi->cbPData = cb;
|
|
mi->pPData = buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetXData(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
DWORD celt;
|
|
LONG count;
|
|
DWORD cb;
|
|
PBYTE buf;
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
VARIANT var;
|
|
|
|
CComPtr< IDiaEnumDebugStreams > idiaStreams;
|
|
CComPtr< IDiaEnumDebugStreamData > idiaStream;
|
|
|
|
assert (mi && mi->dia);
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
hr = pdia->session->getEnumDebugStreams(&idiaStreams);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
var.vt = VT_BSTR;
|
|
var.bstrVal = L"XDATA";
|
|
hr = idiaStreams->Item(var, &idiaStream);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = idiaStream->get_Count(&count);
|
|
if (hr != S_OK)
|
|
return false;
|
|
if (count < 1)
|
|
return true;
|
|
|
|
hr = idiaStream->Next(count, 0, &cb, NULL, &celt);
|
|
if (hr != S_OK)
|
|
return false;
|
|
if (cb < 1)
|
|
return true;
|
|
|
|
CComQIPtr< IDiaImageData, &IID_IDiaImageData > idiaXDataHdr(idiaStream);
|
|
if (!idiaXDataHdr.p)
|
|
return false;
|
|
|
|
DWORD relativeVirtualAddress;
|
|
if (FAILED(hr = idiaXDataHdr->get_relativeVirtualAddress(&relativeVirtualAddress)))
|
|
return false;
|
|
|
|
buf = (PBYTE)MemAlloc(cb + sizeof(DWORD));
|
|
if (!buf)
|
|
return false;
|
|
|
|
memcpy(buf, &relativeVirtualAddress, sizeof(relativeVirtualAddress));
|
|
|
|
hr = idiaStream->Next(count, cb, &cb, buf + sizeof(DWORD), &celt);
|
|
if (hr != S_OK) {
|
|
MemFree(buf);
|
|
return false;
|
|
}
|
|
|
|
mi->dsExceptions = dsDia;
|
|
mi->cXData = count;
|
|
mi->cbXData = cb;
|
|
mi->pXData = buf;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
diaRelease(
|
|
PVOID dia
|
|
)
|
|
{
|
|
PDIA pdia = (PDIA)dia;
|
|
if (pdia)
|
|
delete pdia;
|
|
}
|
|
|
|
|
|
#if 1
|
|
LONG
|
|
diaCountGlobals(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
LONG count;
|
|
LONG rc = 0;
|
|
|
|
CComPtr< IDiaSymbol > idiaGlobals;
|
|
CComPtr< IDiaEnumSymbols > idiaSymbols;
|
|
|
|
if (mi->cGlobals != -1)
|
|
return mi->cGlobals;
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return mi->cGlobals;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaGlobals);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
|
|
// see if there are any globals at all
|
|
|
|
hr = idiaGlobals->findChildren(SymTagData, NULL, 0, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
hr = idiaSymbols->get_Count(&count);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
rc = count;
|
|
|
|
idiaSymbols = NULL;
|
|
hr = idiaGlobals->findChildren(SymTagFunction, NULL, 0, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
hr = idiaSymbols->get_Count(&count);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
rc += count;
|
|
|
|
exit:
|
|
mi->cGlobals = rc;
|
|
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
diaGetPdb(
|
|
PIMGHLP_DEBUG_DATA idd
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD cpathlen = 0;
|
|
DWORD len;
|
|
CHAR szExt[_MAX_EXT + 1] = {0};
|
|
|
|
if (idd->dia) {
|
|
pprint(idd->pe, "redundant pdb call!\n");
|
|
return true;
|
|
}
|
|
|
|
if (*idd->ImageFilePath) {
|
|
_splitpath(idd->ImageFilePath, NULL, NULL, NULL, szExt);
|
|
} else if (*idd->ImageName) {
|
|
_splitpath(idd->ImageName, NULL, NULL, NULL, szExt);
|
|
}
|
|
|
|
// if we have no valid filename, then this must be an executable
|
|
|
|
if (!*szExt)
|
|
CopyStrArray(szExt, ".exe");
|
|
|
|
// get interface to dia
|
|
|
|
pdia = new DIA;
|
|
if (!pdia) {
|
|
hr = E_PDB_OUT_OF_MEMORY;
|
|
goto error;
|
|
}
|
|
idd->dia = pdia;
|
|
|
|
pdia->source = NULL;
|
|
#ifdef COMDIA
|
|
hr = CoCreateInstance(CLSID_DiaSourceAlt, NULL, CLSCTX_INPROC_SERVER, IID_IDiaDataSource, (void **)&pdia->source);
|
|
#else
|
|
hr = DiaCoCreate(CLSID_DiaSourceAlt, IID_IDiaDataSource, (void **)&pdia->source);
|
|
#endif
|
|
if (hr != S_OK)
|
|
goto error;
|
|
|
|
// go ahead and get pdb
|
|
|
|
SetCriticalErrorMode();
|
|
|
|
hr = diaLocatePdb(idd,
|
|
idd->PdbFileName,
|
|
&idd->PdbGUID,
|
|
idd->PdbSignature,
|
|
idd->PdbAge,
|
|
&szExt[1]);
|
|
|
|
ResetCriticalErrorMode();
|
|
|
|
if (hr != S_OK) {
|
|
hr = S_OK; // error was already handled by diaLocatePdb()
|
|
goto error;
|
|
}
|
|
|
|
// open the session on the pdb
|
|
|
|
pdia->session = NULL;
|
|
hr = pdia->source->openSession(&pdia->session);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
|
|
// Set the module load address so we can use VAs.
|
|
hr = pdia->session->put_loadAddress(idd->InProcImageBase);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
|
|
// fixup the address map so that we can translate rva to full addresses
|
|
|
|
hr = pdia->session->QueryInterface(IID_IDiaAddressMap, (void**)&pdia->addrmap);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
|
|
if (idd->pCurrentSections) {
|
|
hr = pdia->addrmap->set_imageHeaders(idd->cCurrentSections * sizeof(IMAGE_SECTION_HEADER),
|
|
(BYTE *)idd->pCurrentSections,
|
|
false);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
}
|
|
|
|
// this hack is to fix a problem with v7 pdbs not storing the original image alignment
|
|
|
|
if (idd->ImageAlign) {
|
|
hr = pdia->addrmap->put_imageAlign(idd->ImageAlign);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
}
|
|
|
|
// pass in the omap information and setup the proper image alignment to the original
|
|
|
|
if (idd->cOmapFrom && idd->pOmapFrom) {
|
|
hr = pdia->addrmap->put_imageAlign(idd->ImageAlign);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
hr = pdia->addrmap->set_addressMap(idd->cOmapTo, (DiaAddressMapEntry *)idd->pOmapTo, true);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
hr = pdia->addrmap->set_addressMap(idd->cOmapFrom, (DiaAddressMapEntry *)idd->pOmapFrom, false);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
hr = pdia->addrmap->put_addressMapEnabled(true);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
}
|
|
|
|
hr = pdia->addrmap->put_relativeVirtualAddressEnabled(true);
|
|
if (hr != S_OK)
|
|
goto error;
|
|
|
|
diaGetFPOTable(idd);
|
|
diaGetOmaps(idd);
|
|
diaGetPdbInfo(idd);
|
|
|
|
return true;
|
|
|
|
error:
|
|
if (hr != S_OK)
|
|
pprint(idd->pe, "%s %s\n", idd->PdbFileName, diaErrorText(hr));
|
|
|
|
diaRelease(pdia);
|
|
idd->dia = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
DWORD64
|
|
GetAddressFromRva(
|
|
PMODULE_ENTRY mi,
|
|
DWORD rva
|
|
)
|
|
{
|
|
DWORD64 addr;
|
|
|
|
assert(mi);
|
|
addr = rva ? mi->BaseOfDll + rva : 0;
|
|
return addr;
|
|
}
|
|
|
|
DWORD64
|
|
GetLineAddressFromRva(
|
|
PMODULE_ENTRY mi,
|
|
DWORD rva
|
|
)
|
|
{
|
|
DWORD64 addr;
|
|
|
|
assert(mi);
|
|
addr = rva ? mi->BaseOfDll + rva : 0;
|
|
|
|
// Line symbol information names the IA64 bundle
|
|
// syllables with 0,1,2 whereas the debugger expects
|
|
// 0,4,8. Convert.
|
|
if (mi->MachineType == IMAGE_FILE_MACHINE_IA64 && (addr & 3)) {
|
|
addr = (addr & ~3) | ((addr & 3) << 2);
|
|
}
|
|
|
|
return addr;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaFillSymbolInfo(
|
|
PSYMBOL_INFO si,
|
|
PMODULE_ENTRY mi,
|
|
IDiaSymbol *idiaSymbol
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
BSTR wname=NULL;
|
|
char name[MAX_SYM_NAME + 1];
|
|
char *p;
|
|
DWORD dw;
|
|
ULONG64 size;
|
|
ULONG64 va;
|
|
BOOL rc;
|
|
VARIANT value;
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaValues;
|
|
CComPtr<IDiaSymbol> idiaValue;
|
|
|
|
if (!idiaSymbol)
|
|
return false;
|
|
|
|
rc = true;
|
|
|
|
dw = si->MaxNameLen;
|
|
ZeroMemory(si, sizeof(SYMBOL_INFO));
|
|
si->MaxNameLen = dw;
|
|
|
|
// si->SizeOfStruct = IGNORED;
|
|
|
|
// si->TypeIndex = NYI;
|
|
|
|
// si->Reserved = IGNORED;
|
|
|
|
si->ModBase = mi->BaseOfDll;
|
|
|
|
hr = idiaSymbol->get_symTag(&si->Tag);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
switch (si->Tag)
|
|
{
|
|
case SymTagData:
|
|
hr = idiaSymbol->get_locationType(&dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
switch(dw)
|
|
{
|
|
case LocIsTLS:
|
|
// TLS variables have an offset into the TLS data area.
|
|
si->Flags = SYMFLAG_TLSREL;
|
|
hr = idiaSymbol->get_addressOffset(&dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
si->Address = (ULONG64) (LONG64) (LONG) dw;
|
|
break;
|
|
|
|
case LocIsStatic:
|
|
hr = idiaSymbol->get_relativeVirtualAddress(&dw);
|
|
si->Address = GetAddressFromRva(mi, dw);
|
|
if (!si->Address)
|
|
rc = false;
|
|
break;
|
|
|
|
case LocIsEnregistered:
|
|
hr = idiaSymbol->get_registerId(&si->Register);
|
|
si->Flags = SYMFLAG_REGISTER;
|
|
break;
|
|
|
|
case LocIsRegRel:
|
|
si->Flags = SYMFLAG_REGREL;
|
|
hr = idiaSymbol->get_registerId(&si->Register);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaSymbol->get_offset((PLONG)&dw);
|
|
si->Address = (ULONG64) (LONG64) (LONG) dw;
|
|
break;
|
|
|
|
case LocIsThisRel:
|
|
// struct members - get_Offset
|
|
default:
|
|
si->Flags |= 0;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case SymTagThunk:
|
|
hr = idiaSymbol->get_targetRelativeVirtualAddress(&dw);
|
|
if (hr == S_OK) {
|
|
si->Value = GetAddressFromRva(mi, dw);
|
|
si->Flags |= SYMFLAG_THUNK;
|
|
}
|
|
// pass through
|
|
case SymTagFunction:
|
|
case SymTagPublicSymbol:
|
|
hr = idiaSymbol->get_relativeVirtualAddress(&dw);
|
|
si->Address = GetAddressFromRva(mi, dw);
|
|
if (si->Address)
|
|
break;
|
|
if (option(SYMOPT_ALLOW_ABSOLUTE_SYMBOLS)) {
|
|
hr = idiaSymbol->get_virtualAddress(&va);
|
|
si->Address = va;
|
|
}
|
|
if (!si->Address)
|
|
rc = false;
|
|
break;
|
|
|
|
case SymTagBlock:
|
|
hr = idiaSymbol->get_relativeVirtualAddress(&dw);
|
|
si->Address = GetAddressFromRva(mi, dw);
|
|
if (!si->Address)
|
|
rc = false;
|
|
return rc;
|
|
|
|
case SymTagAnnotation:
|
|
// Local data search
|
|
hr = idiaSymbol->findChildren(SymTagNull, NULL, nsNone, &idiaValues);
|
|
if (hr != S_OK || !idiaValues)
|
|
break;
|
|
p = si->Name;
|
|
*p = 0;
|
|
while (SUCCEEDED(idiaValues->Next(1, &idiaValue, &dw)) && dw == 1) {
|
|
hr = idiaValue->get_value(&value);
|
|
if (hr != S_OK)
|
|
break;
|
|
wcs2ansi(value.bstrVal, p, si->MaxNameLen - (ULONG)(p - si->Name));
|
|
p += strlen(p) + 1;
|
|
FreeDiaVariant(&value);
|
|
idiaValue = NULL;
|
|
}
|
|
*(p + 1) = 0;
|
|
hr = idiaSymbol->get_relativeVirtualAddress(&dw);
|
|
si->Address = GetAddressFromRva(mi, dw);
|
|
if (!si->Address)
|
|
rc = false;
|
|
// There's no name processing for annotations. We're done.
|
|
return rc;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// check for flags and types
|
|
|
|
hr = idiaSymbol->get_dataKind(&dw);
|
|
if (hr == S_OK) {
|
|
if (dw == DataIsParam)
|
|
si->Flags |= SYMFLAG_PARAMETER;
|
|
else if (dw == DataIsConstant)
|
|
si->Flags = SYMFLAG_CONSTANT;
|
|
}
|
|
|
|
hr = idiaSymbol->get_typeId(&dw);
|
|
if (hr == S_OK)
|
|
si->TypeIndex = dw;
|
|
|
|
// get the name
|
|
|
|
hr = idiaSymbol->get_name(&wname);
|
|
if (hr != S_OK || !wname) {
|
|
if (si->Tag != SymTagThunk)
|
|
return false;
|
|
PrintString(name, DIMA(name), "thunk@%I64x", si->Address);
|
|
CopyString(si->Name, name, si->MaxNameLen);
|
|
} else if (!wname[0]) {
|
|
rc = false;
|
|
} else {
|
|
wcs2ansi(wname, name, MAX_SYM_NAME);
|
|
if ((si->Tag != SymTagPublicSymbol)
|
|
&& !option(SYMOPT_NO_PUBLICS)
|
|
&& strchr(name, '@')) {
|
|
rc = false;
|
|
}
|
|
if (option(SYMOPT_NO_CPP)) {
|
|
while (p = strstr(name, "::")) {
|
|
p[0] = '_';
|
|
p[1] = '_';
|
|
}
|
|
}
|
|
if (*name == '.')
|
|
si->Flags = SYMFLAG_FUNCTION;
|
|
if (option(SYMOPT_UNDNAME)
|
|
&& ((si->Tag == SymTagPublicSymbol) || (si->Tag == SymTagThunk)))
|
|
{
|
|
SymUnDNameInternal(si->Name,
|
|
si->MaxNameLen,
|
|
name,
|
|
strlen(name),
|
|
mi->MachineType,
|
|
true);
|
|
if (si->MaxNameLen > 0) {
|
|
CopyStrArray(name, si->Name);
|
|
} else {
|
|
name[0] = 0;
|
|
}
|
|
} else {
|
|
CopyString(si->Name, name, si->MaxNameLen);
|
|
}
|
|
// let the caller know this is a $$$XXXAA style symbol
|
|
if (strlen(name) == 8 && !strncmp(name, "$$$",3) &&
|
|
isxdigit(name[5]) && isxdigit(name[6]) && isxdigit(name[7]) ) {
|
|
rc = false;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
CopyStrArray(name, mi->si.Name);
|
|
if (traceSubName(name)) // for setting debug breakpoints from DBGHELP_TOKEN
|
|
dtrace("debug(%s)\n", name);
|
|
#endif
|
|
|
|
if (wname)
|
|
LocalFree (wname);
|
|
|
|
// get_length is very expensive on public symbols
|
|
|
|
if (si->Tag == SymTagPublicSymbol)
|
|
return rc;
|
|
|
|
// okay. Get the length.
|
|
|
|
hr = idiaSymbol->get_length(&size);
|
|
if (hr == S_OK)
|
|
si->Size = (ULONG)size;
|
|
else {
|
|
CComPtr <IDiaSymbol> pType;
|
|
if ((hr = idiaSymbol->get_type(&pType)) == S_OK){
|
|
hr = pType->get_length(&size);
|
|
if (hr == S_OK)
|
|
si->Size = (ULONG)size;
|
|
}
|
|
pType = NULL;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaSetModFromIP(
|
|
PPROCESS_ENTRY pe
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
DWORD64 ip;
|
|
DWORD rva;
|
|
PDIA pdia;
|
|
|
|
// get the current IP
|
|
|
|
ip = GetIP(pe);
|
|
if (!ip) {
|
|
pprint(pe, "IP not set!\n");
|
|
return false;
|
|
}
|
|
|
|
// find and load symbols for the module that matches the IP
|
|
|
|
pe->ipmi = GetModFromAddr(pe, ip);
|
|
|
|
if (!pe->ipmi)
|
|
return false;
|
|
|
|
if (!pe->ipmi->dia)
|
|
return false;
|
|
|
|
pdia = (PDIA)pe->ipmi->dia;
|
|
rva = (DWORD)(ip - pe->ipmi->BaseOfDll);
|
|
|
|
CComPtr< IDiaSymbol > idiaScope;
|
|
hr = pdia->session->findSymbolByRVA(rva, SymTagNull, &idiaScope);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = pdia->session->symsAreEquiv(idiaScope, pdia->scope);
|
|
if (hr == S_OK)
|
|
return false;
|
|
|
|
pdia->scope = idiaScope;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
PWCHAR
|
|
ConvertNameForDia(
|
|
LPSTR name,
|
|
PWCHAR wname
|
|
)
|
|
{
|
|
assert (name && wname);
|
|
if (!name || !*name)
|
|
return NULL;
|
|
|
|
ansi2wcs(name, wname, MAX_SYM_NAME);
|
|
|
|
return wname;
|
|
}
|
|
|
|
|
|
VOID
|
|
MakeEmbeddedREStr(
|
|
PCHAR out,
|
|
PCHAR in
|
|
)
|
|
{
|
|
if (*in != '*')
|
|
*out++ = '*';
|
|
|
|
for (; *in; in++, out++)
|
|
*out = *in;
|
|
|
|
if (*(in - 1) != '*')
|
|
*out++ = '*';
|
|
|
|
*out = 0;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetLocals(
|
|
PPROCESS_ENTRY pe,
|
|
LPCSTR name,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode
|
|
)
|
|
{
|
|
PMODULE_ENTRY mi;
|
|
DWORD64 ip;
|
|
DWORD rva;
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
DWORD rc;
|
|
DWORD tag;
|
|
DWORD scope;
|
|
DWORD celt;
|
|
DWORD opt;
|
|
CHAR symname[MAX_SYM_NAME + 1];
|
|
WCHAR wbuf[MAX_SYM_NAME + 1];
|
|
PWCHAR wname;
|
|
|
|
assert(pe);
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbols;
|
|
|
|
opt = option(SYMOPT_CASE_INSENSITIVE) ? nsCaseInRegularExpression : nsRegularExpression;
|
|
|
|
if (option(SYMOPT_PUBLICS_ONLY))
|
|
return true;
|
|
|
|
// get the current scope
|
|
|
|
mi = pe->ipmi;
|
|
if (!mi)
|
|
return false;
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
idiaSymbols = pdia->scope;
|
|
diaFillSymbolInfo(&mi->si, mi, idiaSymbols);
|
|
|
|
PrepRE4Srch(name, symname);
|
|
wname = ConvertNameForDia(symname, wbuf);
|
|
|
|
// loop through all symbols
|
|
|
|
for ( ; idiaSymbols != NULL; ) {
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaEnum;
|
|
// local data search
|
|
hr = idiaSymbols->findChildren(SymTagNull, wname, opt, &idiaEnum);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
idiaSymbols->get_symTag(&scope);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
if (scope == SymTagExe) { // sanity check, never enumerate all exe's symbols
|
|
break;
|
|
}
|
|
// this walks the local symbol list for the loaded enumeration
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol;
|
|
|
|
for (;
|
|
SUCCEEDED(hr = idiaEnum->Next( 1, &idiaSymbol, &celt)) && celt == 1;
|
|
idiaSymbol = NULL)
|
|
{
|
|
ULONG DataKind;
|
|
idiaSymbol->get_symTag(&tag);
|
|
switch (tag)
|
|
{
|
|
case SymTagData:
|
|
case SymTagFunction:
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
continue;
|
|
if (!strcmp(mi->si.Name, "`string'"))
|
|
continue;
|
|
mi->si.Scope = scope;
|
|
mi->si.Flags |= SYMFLAG_LOCAL;
|
|
if (!callback)
|
|
return true;
|
|
if (mi->si.Flags & SYMFLAG_CONSTANT)
|
|
continue;
|
|
rc = DoEnumCallback(pe, &mi->si, mi->si.Size, callback, context, use64, unicode);
|
|
if (!rc) {
|
|
mi->code = ERROR_CANCELLED;
|
|
return rc;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (callback && scope == SymTagFunction) // stop when at function scope
|
|
break;
|
|
|
|
// move to lexical parent
|
|
|
|
CComPtr< IDiaSymbol > idiaParent;
|
|
hr = idiaSymbols->get_lexicalParent(&idiaParent);
|
|
if (hr != S_OK || !idiaParent)
|
|
return false;
|
|
|
|
idiaSymbols = idiaParent;
|
|
}
|
|
|
|
// We reached the end. If we enumerating (I.E. callback != NULL)
|
|
// then return true. If we are searching for a single match,
|
|
// we have failed and should return false;
|
|
|
|
if (callback)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
CompareAddrs(
|
|
const void *addr1,
|
|
const void *addr2
|
|
)
|
|
{
|
|
LONGLONG Diff = *(DWORD64 *)addr1 - *(DWORD64 *)addr2;
|
|
|
|
if (Diff < 0) {
|
|
return -1;
|
|
} else if (Diff > 0) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
PDWORD64
|
|
FindAddr(
|
|
PDWORD64 pAddrs,
|
|
ULONG cAddrs,
|
|
DWORD64 addr
|
|
)
|
|
{
|
|
LONG high;
|
|
LONG low;
|
|
LONG i;
|
|
LONG rc;
|
|
|
|
low = 0;
|
|
high = ((LONG)cAddrs) - 1;
|
|
|
|
while (high >= low) {
|
|
i = (low + high) >> 1;
|
|
rc = CompareAddrs(&addr, &pAddrs[i]);
|
|
|
|
if (rc < 0)
|
|
high = i - 1;
|
|
else if (rc > 0)
|
|
low = i + 1;
|
|
else
|
|
return &pAddrs[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
typedef BOOL
|
|
(CALLBACK *PSYM_LOCALENUMSYMBOL_CALLBACK)(
|
|
PSYMBOL_INFO si,
|
|
ULONG size,
|
|
IDiaSymbol *idiaObj,
|
|
PVOID context
|
|
);
|
|
|
|
|
|
char* dispsymtag(
|
|
ULONG symtag
|
|
)
|
|
{
|
|
static char* names[] =
|
|
{
|
|
"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",
|
|
};
|
|
|
|
if (symtag >= SymTagMax)
|
|
return "<Invalid>";
|
|
else
|
|
return names[symtag];
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaEnumScope(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
DWORD tag,
|
|
char *mask,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode,
|
|
DWORD flags,
|
|
IDiaSymbol *idiaScope,
|
|
int depth
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
DWORD celt;
|
|
DWORD stg;
|
|
BOOL disp;
|
|
char pad[300];
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaSymbols;
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
|
|
ZeroMemory(pad, 300);
|
|
memset(pad, ' ', depth * 3);
|
|
|
|
hr = idiaScope->get_symTag(&stg);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// display all objects within this scope
|
|
|
|
hr = idiaScope->findChildren(SymTagNull, NULL, 0, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
return true;
|
|
|
|
while (SUCCEEDED(hr = idiaSymbols->Next( 1, &idiaSymbol, &celt)) && celt == 1) {
|
|
disp = true;
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
continue;
|
|
if (mi->si.Tag == SymTagBlock)
|
|
CopyString(mi->si.Name, "BLOCK", mi->si.NameLen);
|
|
if (tag && mi->si.Tag != tag)
|
|
disp = false;
|
|
if (strcmpre(mi->si.Name, mask, !option(SYMOPT_CASE_INSENSITIVE)))
|
|
disp = false;
|
|
if (disp)
|
|
peprint(pe, "%s%s: %s\n", pad, dispsymtag(mi->si.Tag), mi->si.Name);
|
|
diaEnumScope(pe,
|
|
mi,
|
|
tag,
|
|
mask,
|
|
callback,
|
|
context,
|
|
use64,
|
|
unicode,
|
|
flags,
|
|
idiaSymbol,
|
|
depth + 1);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOL
|
|
diaGetItems(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
PCSTR name,
|
|
DWORD64 addr,
|
|
DWORD tag,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode,
|
|
DWORD flags
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
CHAR symname[MAX_SYM_NAME + 1];
|
|
|
|
CComPtr< IDiaSymbol > idiaGlobals;
|
|
|
|
// check parameters
|
|
|
|
assert(pe && mi);
|
|
|
|
if (!callback)
|
|
return false;
|
|
|
|
if (!name)
|
|
name = "*";
|
|
PrepRE4Srch(name, symname);
|
|
|
|
// get a session ...
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaGlobals);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// ... and enumerate the global scope
|
|
|
|
return diaEnumScope(pe,
|
|
mi,
|
|
tag,
|
|
symname,
|
|
callback,
|
|
context,
|
|
use64,
|
|
unicode,
|
|
flags,
|
|
idiaGlobals,
|
|
1);
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetSymbolsByTag(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
PCSTR name,
|
|
DWORD64 addr,
|
|
DWORD tag,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode,
|
|
DWORD flags
|
|
)
|
|
{
|
|
BOOL rc;
|
|
DWORD opt;
|
|
BOOL fCase;
|
|
PWCHAR wname;
|
|
WCHAR wbuf[MAX_SYM_NAME + 1];
|
|
CHAR symname[MAX_SYM_NAME + 1];
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
DWORD celt;
|
|
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
CComPtr< IDiaSymbol > idiaGlobals;
|
|
CComPtr< IDiaEnumSymbols > idiaSymbols;
|
|
|
|
if (flags & SYMENUMFLAG_FULLSRCH)
|
|
return diaGetItems(pe, mi, name, addr, tag, callback, context, use64, unicode, flags);
|
|
|
|
// check parameters
|
|
|
|
if (!name)
|
|
name = "*";
|
|
|
|
assert(pe && mi);
|
|
|
|
if (!callback && !name)
|
|
return false;
|
|
|
|
if (option(SYMOPT_CASE_INSENSITIVE)) {
|
|
opt = nsCaseInsensitive;
|
|
fCase = false;
|
|
} else {
|
|
opt = nsCaseSensitive;
|
|
fCase = true;
|
|
};
|
|
|
|
if (PrepRE4Srch(name, symname))
|
|
opt |= nsfRegularExpression;
|
|
|
|
wname = ConvertNameForDia(symname, wbuf);
|
|
|
|
// get a session
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaGlobals);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// presume we find nothing
|
|
|
|
rc = false;
|
|
|
|
hr = idiaGlobals->findChildren((enum SymTagEnum)tag, wname, opt, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
for (;
|
|
SUCCEEDED(hr = idiaSymbols->Next( 1, &idiaSymbol, &celt)) && celt == 1;
|
|
idiaSymbol = NULL)
|
|
{
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
continue;
|
|
mi->si.Scope = SymTagExe;
|
|
if (!callback)
|
|
return true;
|
|
if (addr && (mi->si.Address != addr))
|
|
continue;
|
|
if (flags & SYMENUMFLAG_SPEEDSRCH) {
|
|
PSYM_LOCALENUMSYMBOL_CALLBACK cb = (PSYM_LOCALENUMSYMBOL_CALLBACK)callback;
|
|
rc = cb(&mi->si, mi->si.Size, idiaSymbol, context);
|
|
} else
|
|
rc = DoEnumCallback(pe, &mi->si, mi->si.Size, callback, context, use64, unicode);
|
|
if (!rc) {
|
|
mi->code = ERROR_CANCELLED;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
PSYMBOL_INFO
|
|
diaGetSymFromToken(
|
|
PMODULE_ENTRY mi,
|
|
DWORD token
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
|
|
// check parameters
|
|
|
|
assert(mi);
|
|
|
|
// get a session
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
hr = pdia->session->findSymbolByToken(token, SymTagFunction, &idiaSymbol);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
return NULL;
|
|
|
|
return &mi->si;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetGlobals(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
LPCSTR name,
|
|
DWORD64 addr,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode
|
|
)
|
|
{
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
DWORD tag;
|
|
DWORD celt;
|
|
DWORD rc;
|
|
LONG cFuncs;
|
|
LONG cGlobals = 0;
|
|
enum SymTagEnum SearchTag;
|
|
PDWORD64 pGlobals = NULL;
|
|
PDWORD64 pg = NULL;
|
|
PWCHAR wname;
|
|
DWORD opt;
|
|
WCHAR wbuf[MAX_SYM_NAME + 1];
|
|
CHAR symname[MAX_SYM_NAME + 1];
|
|
CHAR pname[MAX_SYM_NAME + 1];
|
|
BOOL fCase;
|
|
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
CComPtr< IDiaSymbol > idiaGlobals;
|
|
CComPtr< IDiaEnumSymbols > idiaSymbols;
|
|
|
|
// check parameters
|
|
|
|
assert(pe && mi && name);
|
|
|
|
if (!callback && !name)
|
|
return false;
|
|
|
|
if (option(SYMOPT_CASE_INSENSITIVE)) {
|
|
opt = nsCaseInsensitive;
|
|
fCase = false;
|
|
} else {
|
|
opt = nsCaseSensitive;
|
|
fCase = true;
|
|
};
|
|
|
|
if (PrepRE4Srch(name, symname))
|
|
opt |= nsfRegularExpression;
|
|
|
|
wname = ConvertNameForDia(symname, wbuf);
|
|
|
|
// get a session
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaGlobals);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// presume we find nothing
|
|
|
|
rc = false;
|
|
|
|
// see if there are any globals at all
|
|
// skip normal symbols, if so required
|
|
|
|
if (option(SYMOPT_PUBLICS_ONLY))
|
|
goto publics;
|
|
|
|
// if this is an enumeration, we will have to store a list of the addresses
|
|
// of all the symbols we found in the global scope. Later we will compare
|
|
// this to the publics so as to eliminate doubles.
|
|
|
|
if (callback) {
|
|
hr = idiaGlobals->findChildren(SymTagData, wname, opt, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaSymbols->get_Count(&cGlobals);
|
|
if (hr != S_OK)
|
|
return false;
|
|
idiaSymbols = NULL;
|
|
|
|
hr = idiaGlobals->findChildren(SymTagFunction, wname, opt, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaSymbols->get_Count(&cFuncs);
|
|
if (hr != S_OK)
|
|
return false;
|
|
idiaSymbols = NULL;
|
|
cGlobals += cFuncs;
|
|
|
|
pGlobals = (PDWORD64)MemAlloc(cGlobals * sizeof(DWORD64));
|
|
}
|
|
|
|
if (callback && (!cGlobals || !pGlobals))
|
|
goto publics;
|
|
|
|
ZeroMemory(pGlobals, cGlobals * sizeof(DWORD64));
|
|
|
|
// First search for data
|
|
SearchTag = SymTagData;
|
|
hr = idiaGlobals->findChildren(SearchTag, wname, opt, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
goto publics;
|
|
|
|
for (pg = pGlobals;
|
|
(SUCCEEDED(hr = idiaSymbols->Next( 1, &idiaSymbol, &celt)) && celt == 1) || (SearchTag == SymTagData);
|
|
idiaSymbol = NULL)
|
|
{
|
|
ULONG DataKind;
|
|
|
|
if ((SearchTag == SymTagData) && (FAILED(hr) || celt != 1)) {
|
|
// Now search for functions
|
|
SearchTag = SymTagFunction;
|
|
idiaSymbols = NULL;
|
|
hr = idiaGlobals->findChildren(SearchTag, wname, opt, &idiaSymbols);
|
|
if (hr == S_OK)
|
|
continue;
|
|
}
|
|
|
|
idiaSymbol->get_symTag(&tag);
|
|
switch (tag)
|
|
{
|
|
case SymTagData:
|
|
case SymTagFunction:
|
|
assert(!callback || ((LONG)(pg - pGlobals) < cGlobals));
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
continue;
|
|
if (!strcmp(mi->si.Name, "`string'"))
|
|
continue;
|
|
mi->si.Scope = SymTagExe;
|
|
if (!callback)
|
|
return true;
|
|
if (mi->si.Flags & SYMFLAG_CONSTANT)
|
|
continue;
|
|
if (addr && (mi->si.Address != addr))
|
|
continue;
|
|
if (pg)
|
|
*pg++ = mi->si.Address;
|
|
rc = DoEnumCallback(pe, &mi->si, mi->si.Size, callback, context, use64, unicode);
|
|
if (!rc) {
|
|
mi->code = ERROR_CANCELLED;
|
|
goto exit;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
qsort(pGlobals, cGlobals, sizeof(DWORD64), CompareAddrs);
|
|
|
|
publics:
|
|
if (option(SYMOPT_NO_PUBLICS))
|
|
goto exit;
|
|
if (option(SYMOPT_AUTO_PUBLICS) && cGlobals && !IsRegularExpression(name))
|
|
goto exit;
|
|
|
|
// now check out the publics table
|
|
|
|
if (wname) {
|
|
PrintString(pname, DIMA(pname), "*%s*", symname);
|
|
MakeEmbeddedREStr(pname, symname);
|
|
wname = ConvertNameForDia(pname, wbuf);
|
|
}
|
|
|
|
idiaSymbols = NULL;
|
|
|
|
opt |= nsfUndecoratedName | nsfRegularExpression;
|
|
|
|
hr = idiaGlobals->findChildren(SymTagPublicSymbol, wname, opt, &idiaSymbols);
|
|
if (hr != S_OK)
|
|
goto exit;
|
|
|
|
for (;
|
|
SUCCEEDED(hr = idiaSymbols->Next( 1, &idiaSymbol, &celt)) && celt == 1;
|
|
idiaSymbol = NULL)
|
|
{
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol))
|
|
continue;
|
|
mi->si.Scope = SymTagPublicSymbol;
|
|
if (!strcmp(mi->si.Name, "`string'"))
|
|
continue;
|
|
// publics names are mangled: this tests the undecorated name against the mask
|
|
if (*name && strcmpre(mi->si.Name, name, fCase))
|
|
continue;
|
|
if (!callback)
|
|
return true;
|
|
if (FindAddr(pGlobals, cGlobals, mi->si.Address))
|
|
continue;
|
|
if (addr && (mi->si.Address != addr))
|
|
continue;
|
|
rc = DoEnumCallback(pe, &mi->si, mi->si.Size, callback, context, use64, unicode);
|
|
if (!rc) {
|
|
mi->code = ERROR_CANCELLED;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// We reached the end. If we are not enumerating (I.E. callback == NULL)
|
|
// then return the result of the last call to the callback. If we are
|
|
// searching for a single match, we have failed and should return false;
|
|
|
|
exit:
|
|
MemFree(pGlobals);
|
|
if (!callback)
|
|
return false;
|
|
return rc;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetSymbols(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
PCSTR name,
|
|
PROC callback,
|
|
PVOID context,
|
|
BOOL use64,
|
|
BOOL unicode
|
|
)
|
|
{
|
|
// ENUMFIX:
|
|
LPCSTR pname = (name) ? name : "";
|
|
|
|
if (mi) {
|
|
return diaGetGlobals(pe, mi, pname, 0, callback, context, use64, unicode);
|
|
} else {
|
|
return diaGetLocals(pe, pname, callback, context, use64, unicode);
|
|
}
|
|
}
|
|
|
|
|
|
PSYMBOL_INFO
|
|
diaFindSymbolByName(
|
|
PPROCESS_ENTRY pe,
|
|
PMODULE_ENTRY mi,
|
|
LPSTR SymName
|
|
)
|
|
{
|
|
if (!diaGetSymbols(pe, mi, SymName, NULL, NULL, 0, 0))
|
|
return NULL;
|
|
|
|
if (!mi)
|
|
mi = pe->ipmi;
|
|
|
|
return &mi->si;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaEnumerateSymbols(
|
|
IN PPROCESS_ENTRY pe,
|
|
IN PMODULE_ENTRY mi,
|
|
IN PCSTR mask,
|
|
IN PROC callback,
|
|
IN PVOID context,
|
|
IN BOOL use64,
|
|
IN BOOL unicode
|
|
)
|
|
{
|
|
return diaGetSymbols(pe, mi, mask, callback, context, use64, unicode);
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaEnumSymForAddr(
|
|
IN PPROCESS_ENTRY pe,
|
|
IN PMODULE_ENTRY mi,
|
|
IN DWORD64 addr,
|
|
IN PROC callback,
|
|
IN PVOID context,
|
|
IN BOOL use64,
|
|
IN BOOL unicode
|
|
)
|
|
{
|
|
return diaGetGlobals(pe, mi, "", addr, callback, context, use64, unicode);
|
|
}
|
|
|
|
|
|
PSYMBOL_INFO
|
|
diaGetSymFromAddr(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
PDWORD64 disp
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
DWORD tag;
|
|
LONG omapadj;
|
|
BOOL fHitBlock;
|
|
|
|
// simple sanity check
|
|
|
|
if (!addr)
|
|
return NULL;
|
|
|
|
assert (mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
#ifdef DEBUG
|
|
if (traceAddr(addr)) // for debug breakpoints ...
|
|
dtrace("found 0x%I64x\n", addr);
|
|
#endif
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
// get the symbol
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol = NULL;
|
|
|
|
fHitBlock = false;
|
|
if (!option(SYMOPT_PUBLICS_ONLY)) {
|
|
|
|
hr = pdia->session->findSymbolByRVAEx(rva, SymTagNull, &idiaSymbol, &omapadj);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
// if the symbol is a block, keep grabbing the parent
|
|
// until we get a function...
|
|
|
|
idiaSymbol->get_symTag(&tag);
|
|
while (tag == SymTagBlock) { // SymTagLabel as well?
|
|
CComPtr< IDiaSymbol > idiaParent;
|
|
fHitBlock = true;
|
|
hr = idiaSymbol->get_lexicalParent(&idiaParent);
|
|
if (hr != S_OK || !idiaParent)
|
|
return NULL;
|
|
idiaSymbol = idiaParent;
|
|
idiaSymbol->get_symTag(&tag);
|
|
}
|
|
|
|
}
|
|
|
|
if (option(SYMOPT_NO_PUBLICS))
|
|
return NULL;
|
|
|
|
if (!diaFillSymbolInfo(&mi->si, mi, idiaSymbol)) {
|
|
// return a public symbol
|
|
idiaSymbol = NULL;
|
|
hr = pdia->session->findSymbolByRVAEx(rva, SymTagPublicSymbol, &idiaSymbol, &omapadj);
|
|
if (hr == S_OK)
|
|
diaFillSymbolInfo(&mi->si, mi, idiaSymbol);
|
|
}
|
|
|
|
if (disp)
|
|
*disp = (fHitBlock) ? addr - mi->si.Address : omapadj;
|
|
|
|
return &mi->si;
|
|
}
|
|
|
|
|
|
PSYMBOL_INFO
|
|
diaGetSymFromAddrByTag(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
DWORD tag,
|
|
PDWORD64 disp
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
LONG omapadj;
|
|
|
|
// simple sanity check
|
|
|
|
if (!addr)
|
|
return NULL;
|
|
|
|
assert (mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
// get the symbol
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol = NULL;
|
|
|
|
hr = pdia->session->findSymbolByRVAEx(rva, (enum SymTagEnum)tag, &idiaSymbol, &omapadj);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
diaFillSymbolInfo(&mi->si, mi, idiaSymbol);
|
|
if (disp)
|
|
*disp = addr - mi->si.Address;
|
|
|
|
return &mi->si;
|
|
}
|
|
|
|
|
|
typedef struct _ENUMOBJS {
|
|
PPROCESS_ENTRY pe;
|
|
PMODULE_ENTRY mi;
|
|
PSYM_ENUMLINES_CALLBACK cb;
|
|
PCSTR file;
|
|
PVOID context;
|
|
} ENUMOBJS, *PENUMOBJS;
|
|
|
|
BOOL
|
|
cbEnumObjs(
|
|
PSYMBOL_INFO si,
|
|
ULONG size,
|
|
IDiaSymbol *idiaObj,
|
|
PVOID context
|
|
)
|
|
{
|
|
WCHAR wbuf[MAX_PATH + 1];
|
|
BSTR wfname = NULL;
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
PMODULE_ENTRY mi;
|
|
PENUMOBJS eno = (PENUMOBJS)context;
|
|
PSYM_ENUMLINES_CALLBACK cb;
|
|
|
|
WCHAR wsz[_MAX_PATH + 1];
|
|
char file[MAX_PATH + 1];
|
|
BSTR bstr;
|
|
DWORD dw;
|
|
BOOL rc;
|
|
|
|
CComPtr< IDiaEnumSourceFiles > idiaSrcFiles = NULL;
|
|
CComPtr< IDiaSourceFile > idiaSrcFile = NULL;
|
|
CComPtr< IDiaEnumLineNumbers > idiaLines = NULL;
|
|
CComPtr< IDiaLineNumber > idiaLine = NULL;
|
|
|
|
dtrace("%s\n", si->Name);
|
|
|
|
// get initial data and store the obj name
|
|
|
|
pdia = (PDIA)eno->mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
cb = (PSYM_ENUMLINES_CALLBACK)eno->cb;
|
|
mi = eno->mi;
|
|
if (!mi)
|
|
return false;
|
|
if (!*si->Name)
|
|
return false;
|
|
CopyString(mi->sci.Obj, si->Name, MAX_PATH + 1);
|
|
mi->sci.ModBase = mi->BaseOfDll;
|
|
|
|
// prepare the source file name mask
|
|
|
|
wfname = NULL;
|
|
if (eno->file && *eno->file) {
|
|
ansi2wcs(eno->file, wbuf, MAX_PATH);
|
|
wfname = wbuf;
|
|
}
|
|
|
|
// get all the source files
|
|
|
|
hr = pdia->session->findFile(idiaObj, wfname, nsCaseInsensitive, &idiaSrcFiles);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
while (idiaSrcFiles->Next(1, &idiaSrcFile, &dw) == S_OK) {
|
|
|
|
hr = idiaSrcFile->get_fileName(&bstr);
|
|
if (hr != S_OK)
|
|
break;
|
|
|
|
rc = wcs2ansi(bstr, mi->sci.FileName, MAX_PATH + 1);
|
|
LocalFree(bstr);
|
|
if (!rc || !*mi->sci.FileName)
|
|
break;
|
|
|
|
hr = pdia->session->findLines(idiaObj, idiaSrcFile, &idiaLines);
|
|
if (hr != S_OK)
|
|
break;
|
|
|
|
while (idiaLines->Next(1, &idiaLine, &dw) == S_OK) {
|
|
hr = idiaLine->get_lineNumber(&mi->sci.LineNumber);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaLine->get_relativeVirtualAddress(&dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
mi->sci.Address = dw + mi->BaseOfDll;
|
|
idiaLine = NULL;
|
|
if (cb)
|
|
rc = cb(&mi->sci, context);
|
|
}
|
|
|
|
idiaLines = NULL;
|
|
idiaSrcFile = NULL;
|
|
}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaEnumLines(
|
|
IN PPROCESS_ENTRY pe,
|
|
IN PMODULE_ENTRY mi,
|
|
IN PCSTR obj,
|
|
IN PCSTR file,
|
|
IN PSYM_ENUMLINES_CALLBACK cb,
|
|
IN PVOID context
|
|
)
|
|
{
|
|
ENUMOBJS eno;
|
|
|
|
eno.pe = pe;
|
|
eno.mi = mi;
|
|
eno.cb = cb;
|
|
eno.file = file;
|
|
eno.context = context;
|
|
|
|
return diaGetSymbolsByTag(pe,
|
|
mi,
|
|
obj,
|
|
0,
|
|
SymTagCompiland,
|
|
(PROC)cbEnumObjs,
|
|
&eno,
|
|
false,
|
|
false,
|
|
SYMENUMFLAG_SPEEDSRCH);
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetLineFromAddr(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
PDWORD displacement,
|
|
PSRCCODEINFO sci
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
DWORD celt;
|
|
BSTR bstr;
|
|
DWORD dw;
|
|
BOOL rc;
|
|
|
|
assert(mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
rva = (DWORD)(addr - mi->BaseOfDll);
|
|
|
|
// On IA64 the slots in a bundle don't have byte addresses.
|
|
// The debugger calls them 0,4,8 by default whereas line
|
|
// symbols have them as 0,1,2. Convert to line style
|
|
// before querying.
|
|
if (mi->MachineType == IMAGE_FILE_MACHINE_IA64 && (rva & 0xf)) {
|
|
switch(rva & 0xf) {
|
|
case 4:
|
|
rva -= 3;
|
|
break;
|
|
case 8:
|
|
rva -= 6;
|
|
break;
|
|
default:
|
|
// Invalid slot address.
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CComPtr< IDiaEnumLineNumbers > idiaLines = NULL;
|
|
hr = pdia->session->findLinesByRVA(rva, 1, &idiaLines);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
CComPtr< IDiaLineNumber > idiaLine = NULL;
|
|
hr = idiaLines->Next(1, &idiaLine, &celt);
|
|
if (hr != S_OK || !idiaLine)
|
|
return false;
|
|
|
|
hr = idiaLine->get_lineNumber(&dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
sci->LineNumber = dw;
|
|
|
|
pdia->srcfile = NULL;
|
|
hr = idiaLine->get_sourceFile(&pdia->srcfile);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = pdia->srcfile->get_fileName(&bstr);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
*sci->FileName = 0;
|
|
rc = wcs2ansi(bstr, sci->FileName, DIMA(sci->FileName));
|
|
LocalFree(bstr);
|
|
if (!rc || !*sci->FileName)
|
|
return false;
|
|
|
|
hr = idiaLine->get_relativeVirtualAddress(&dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
sci->Address = dw + mi->BaseOfDll;
|
|
*displacement = rva - dw;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetNextLineFromEnum(
|
|
IDiaEnumLineNumbers *idiaLines,
|
|
DWORD *line,
|
|
DWORD *rva
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
ULONG ul;
|
|
|
|
CComPtr< IDiaLineNumber > idiaLine = NULL;
|
|
|
|
hr = idiaLines->Next(1, &idiaLine, &ul);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = idiaLine->get_lineNumber(line);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = idiaLine->get_relativeVirtualAddress(rva);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
BOOL
|
|
diaGetLineNextPrev(
|
|
PMODULE_ENTRY mi,
|
|
PIMAGEHLP_LINE64 line,
|
|
DWORD direction
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
DWORD celt;
|
|
WCHAR wbuf[MAX_PATH + 1];
|
|
BSTR wfname = NULL;
|
|
DWORD64 bAddr;
|
|
DWORD trgnum;
|
|
DWORD num;
|
|
DWORD dw;
|
|
DWORD num1 = 0;
|
|
DWORD num2 = 0;
|
|
DWORD rva1 = 0;
|
|
DWORD rva2 = 0;
|
|
LONG numlines;
|
|
|
|
// simple sanity checks
|
|
|
|
assert(mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
assert(direction == NP_NEXT || direction == NP_PREV);
|
|
|
|
if (line->SizeOfStruct != sizeof(IMAGEHLP_LINE64))
|
|
return false;
|
|
|
|
// convert file name for DIA
|
|
|
|
if (!*line->FileName)
|
|
return false;
|
|
|
|
ansi2wcs(line->FileName, wbuf, MAX_PATH);
|
|
wfname = wbuf;
|
|
|
|
// save the last found line
|
|
|
|
bAddr = line->Address;
|
|
|
|
// all source files in the module that match the 'wfname'
|
|
|
|
CComPtr< IDiaEnumSourceFiles > idiaSrcFiles = NULL;
|
|
hr = pdia->session->findFile(NULL, wfname, nsCaseInsensitive, &idiaSrcFiles);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// the first such file in the list, since we don't use wildcards
|
|
|
|
CComPtr< IDiaSourceFile > idiaSrcFile = NULL;
|
|
hr = idiaSrcFiles->Next(1, &idiaSrcFile, &dw);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// all objs that use this source file
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaObjs = NULL;
|
|
hr = idiaSrcFile->get_compilands(&idiaObjs);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// LOOP THROUGH ALL THE OBJS! AND STORE THE CLOSEST!
|
|
|
|
num = 0;
|
|
rva = 0;
|
|
|
|
// grab the first obj, since we don't care
|
|
|
|
CComPtr< IDiaSymbol > idiaObj = NULL;
|
|
CComPtr< IDiaEnumLineNumbers > idiaLines = NULL;
|
|
|
|
hr = idiaObjs->Next(1, &idiaObj, &celt);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// get the line for starting with
|
|
|
|
trgnum = line->LineNumber + direction;
|
|
hr = pdia->session->findLinesByLinenum(idiaObj, idiaSrcFile, trgnum, 0, &idiaLines);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
hr = idiaLines->get_Count(&numlines);
|
|
diaGetNextLineFromEnum(idiaLines, &num1, &rva1);
|
|
if (numlines > 1)
|
|
diaGetNextLineFromEnum(idiaLines, &num2, &rva2);
|
|
|
|
if (direction == NP_PREV) {
|
|
num = num1;
|
|
rva = rva1;
|
|
} else if (num1 == trgnum) {
|
|
num = num1;
|
|
rva = rva1;
|
|
} else {
|
|
num = num2;
|
|
rva = rva2;
|
|
}
|
|
|
|
if (!num)
|
|
return false;
|
|
|
|
if (bAddr == GetLineAddressFromRva(mi, rva))
|
|
return false;
|
|
|
|
line->LineNumber = num;
|
|
line->Address = GetLineAddressFromRva(mi, rva);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#if 0
|
|
#define DBG_DIA_LINE 1
|
|
#endif
|
|
|
|
BOOL
|
|
diaGetLineFromName(
|
|
PMODULE_ENTRY mi,
|
|
LPSTR filename,
|
|
DWORD linenumber,
|
|
PLONG displacement,
|
|
PSRCCODEINFO sci,
|
|
DWORD method
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR wsz[_MAX_PATH + 1];
|
|
char sz[MAX_PATH + 1];
|
|
PDIA pdia;
|
|
DWORD celt;
|
|
BSTR bstr;
|
|
DWORD addr;
|
|
DWORD num;
|
|
BOOL rc;
|
|
DWORD flags;
|
|
LONG cFiles;
|
|
int i;
|
|
|
|
CComPtr<IDiaEnumSourceFiles> idiaSrcFiles;
|
|
CComPtr<IDiaSourceFile> idiaSrcFile;
|
|
CComPtr<IDiaEnumSymbols> idiaEnum;
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
CComPtr<IDiaEnumLineNumbers> idiaLineNumbers;
|
|
CComPtr<IDiaLineNumber> idiaLineNumber;
|
|
|
|
assert(mi && mi->dia && filename);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
sciInit(sci);
|
|
|
|
if (!ansi2wcs(filename, wsz, DIMA(wsz)))
|
|
return false;
|
|
if (!*wsz)
|
|
return false;
|
|
|
|
flags = (method == mFullPath) ? nsfCaseInsensitive : nsFNameExt;
|
|
|
|
// get list of matching files and the count of the list
|
|
|
|
hr = pdia->session->findFile(NULL, wsz, flags, &idiaSrcFiles);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaSrcFiles->get_Count(&cFiles);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
*sci->FileName = 0;
|
|
for (i = 0; i < cFiles; i++) {
|
|
hr = idiaSrcFiles->Next(1, &idiaSrcFile, &celt);
|
|
if (hr != S_OK)
|
|
continue;
|
|
|
|
hr = idiaSrcFile->get_fileName(&bstr);
|
|
if (hr != S_OK)
|
|
continue;
|
|
|
|
rc = wcs2ansi(bstr, sz, DIMA(sz));
|
|
LocalFree(bstr);
|
|
if (!rc || !*sz)
|
|
continue;
|
|
|
|
UpdateBestSrc(filename, sci->FileName, sz);
|
|
}
|
|
|
|
if (!*sci->FileName)
|
|
return false;
|
|
|
|
// this gives us a list of every .obj that uses this source file
|
|
|
|
hr = idiaSrcFile->get_compilands(&idiaEnum);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// we don't support multiple objs, so lets take the first one
|
|
|
|
hr = idiaEnum->Next(1, &idiaSymbol, &celt);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
// This gets a list of all code items that were created from this source line.
|
|
// If we want to fully support inlines and the like, we need to loop all of these
|
|
|
|
hr = pdia->session->findLinesByLinenum(idiaSymbol, idiaSrcFile, linenumber, 0, &idiaLineNumbers);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
idiaLineNumber = NULL;
|
|
hr = idiaLineNumbers->Next(1, &idiaLineNumber, &celt);
|
|
if (hr != S_OK)
|
|
return false;
|
|
hr = idiaLineNumber->get_lineNumber(&num);
|
|
if (hr != S_OK)
|
|
return false;
|
|
sci->LineNumber = num;
|
|
hr = idiaLineNumber->get_relativeVirtualAddress(&addr);
|
|
if (hr != S_OK)
|
|
return false;
|
|
if (!addr)
|
|
return false;
|
|
|
|
sci->Address = GetLineAddressFromRva(mi, addr);
|
|
*displacement = linenumber - num;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
MatchSourceFile(
|
|
PCHAR filename,
|
|
PCHAR mask
|
|
)
|
|
{
|
|
PCHAR p;
|
|
|
|
if (!mask || !*mask)
|
|
return true;
|
|
|
|
if (!*filename)
|
|
return false;
|
|
|
|
for (p = filename + strlen(filename); p >= filename; p--) {
|
|
if (*p == '\\' || *p == '/') {
|
|
p++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!strcmpre(p, mask, false))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
BOOL
|
|
diaEnumSourceFiles(
|
|
IN PMODULE_ENTRY mi,
|
|
IN PCHAR mask,
|
|
IN PSYM_ENUMSOURCFILES_CALLBACK cbSrcFiles,
|
|
IN PVOID context
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
BSTR wname=NULL;
|
|
char name[_MAX_PATH + 1];
|
|
SOURCEFILE sf;
|
|
|
|
assert(mi && cbSrcFiles);
|
|
|
|
PDIA pdia;
|
|
pdia = (PDIA)mi->dia;
|
|
|
|
sf.ModBase = mi->BaseOfDll ;
|
|
sf.FileName = name;
|
|
|
|
CComPtr< IDiaEnumSourceFiles > idiaEnumFiles;
|
|
hr = pdia->session->findFile(NULL, NULL, nsNone, &idiaEnumFiles);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
ULONG celt;
|
|
CComPtr <IDiaSourceFile> idiaSource;
|
|
for (;SUCCEEDED(idiaEnumFiles->Next(1, &idiaSource, &celt)) && (celt == 1);) {
|
|
hr = idiaSource->get_fileName(&wname);
|
|
if (hr == S_OK && wname) {
|
|
wcs2ansi(wname, name, _MAX_PATH);
|
|
LocalFree (wname);
|
|
if (MatchSourceFile(name, mask)) {
|
|
if (!cbSrcFiles(&sf, context)) {
|
|
mi->code = ERROR_CANCELLED;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
idiaSource = NULL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
PSYMBOL_INFO
|
|
diaGetSymNextPrev(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 addr,
|
|
int direction
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
DWORD celt;
|
|
|
|
assert(mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
CComPtr<IDiaEnumSymbolsByAddr> idiaSymbols;
|
|
hr = pdia->session->getSymbolsByAddr(&idiaSymbols);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
rva = addr ? (DWORD)(addr - mi->BaseOfDll) : 0;
|
|
|
|
CComPtr<IDiaSymbol> idiaSymbol;
|
|
hr = idiaSymbols->symbolByRVA(rva, &idiaSymbol);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
findsymbol:
|
|
if (addr) {
|
|
if (direction < 0) {
|
|
idiaSymbol = NULL;
|
|
hr = idiaSymbols->Prev(1, &idiaSymbol, &celt);
|
|
} else {
|
|
idiaSymbol = NULL;
|
|
hr = idiaSymbols->Next(1, &idiaSymbol, &celt);
|
|
}
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
if (celt != 1)
|
|
return NULL;
|
|
}
|
|
|
|
diaFillSymbolInfo(&mi->si, mi, idiaSymbol);
|
|
if (!*mi->si.Name) {
|
|
rva = (DWORD)(mi->si.Address - mi->BaseOfDll);
|
|
goto findsymbol;
|
|
}
|
|
|
|
return &mi->si;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
diaGetSymTag(IDiaSymbol *pType, PULONG pTag)
|
|
{
|
|
return pType->get_symTag(pTag);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetSymIndexId(IDiaSymbol *pType, PULONG pIndex)
|
|
{
|
|
return pType->get_symIndexId(pIndex);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetLexicalParentId(IDiaSymbol *pType, PULONG pIndex)
|
|
{
|
|
return pType->get_lexicalParentId(pIndex);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetDataKind(IDiaSymbol *pType, PULONG pKind)
|
|
{
|
|
return pType->get_dataKind(pKind);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetSymName(IDiaSymbol *pType, BSTR *pname)
|
|
{
|
|
return pType->get_name(pname);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
diaGetLength(IDiaSymbol *pType, PULONGLONG pLength)
|
|
{
|
|
return pType->get_length(pLength);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetType(IDiaSymbol *pType, IDiaSymbol ** pSymbol)
|
|
{
|
|
return pType->get_type(pSymbol);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetBaseType(IDiaSymbol *pType, PULONG pBase)
|
|
{
|
|
return pType->get_baseType(pBase);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetArrayIndexTypeId(IDiaSymbol *pType, PULONG pSymbol)
|
|
{
|
|
return pType->get_arrayIndexTypeId(pSymbol);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetTypeId(IDiaSymbol *pType, PULONG pTypeId)
|
|
{
|
|
return pType->get_typeId(pTypeId);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetCallingConvention(IDiaSymbol *pType, PULONG pConvention)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = pType->get_callingConvention(pConvention);
|
|
if (hr != S_OK)
|
|
*pConvention = CV_CALL_RESERVED;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
diaGetChildrenCount(IDiaSymbol *pType, LONG *pCount)
|
|
{
|
|
CComPtr <IDiaEnumSymbols> pEnum;
|
|
HRESULT hr;
|
|
ULONG index;
|
|
CComPtr <IDiaSymbol> pSym;
|
|
ULONG Count;
|
|
|
|
if ((hr = pType->findChildren(SymTagNull, NULL, nsNone, &pEnum)) != S_OK) {
|
|
return hr;
|
|
}
|
|
return pEnum->get_Count(pCount);
|
|
}
|
|
|
|
HRESULT
|
|
diaFindChildren(IDiaSymbol *pType, TI_FINDCHILDREN_PARAMS *Params)
|
|
{
|
|
CComPtr <IDiaEnumSymbols> pEnum;
|
|
HRESULT hr;
|
|
ULONG index;
|
|
CComPtr <IDiaSymbol> pSym;
|
|
ULONG Count;
|
|
|
|
if ((hr = pType->findChildren(SymTagNull, NULL, nsNone, &pEnum)) != S_OK) {
|
|
return hr;
|
|
}
|
|
|
|
VARIANT var;
|
|
|
|
pEnum->Skip(Params->Start);
|
|
for (Count = Params->Count, index = Params->Start; Count > 0; Count--, index++) {
|
|
ULONG celt;
|
|
pSym = NULL;
|
|
if ((hr = pEnum->Next(1, &pSym, &celt)) != S_OK) {
|
|
return hr;
|
|
}
|
|
|
|
if ((hr = pSym->get_symIndexId(&Params->ChildId[index])) != S_OK) {
|
|
return hr;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
diaGetAddressOffset(IDiaSymbol *pType, ULONG *pOff)
|
|
{
|
|
return pType->get_addressOffset(pOff);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetOffset(IDiaSymbol *pType, LONG *pOff)
|
|
{
|
|
return pType->get_offset(pOff);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetValue(IDiaSymbol *pType, VARIANT *pVar)
|
|
{
|
|
return pType->get_value(pVar);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetCount(IDiaSymbol *pType, ULONG *pCount)
|
|
{
|
|
return pType->get_count(pCount);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetBitPosition(IDiaSymbol *pType, ULONG *pPos)
|
|
{
|
|
return pType->get_bitPosition(pPos);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetVirtualBaseClass(IDiaSymbol *pType, BOOL *pBase)
|
|
{
|
|
return pType->get_virtualBaseClass(pBase);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetVirtualTableShapeId(IDiaSymbol *pType, PULONG pShape)
|
|
{
|
|
return pType->get_virtualTableShapeId(pShape);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetVirtualBasePointerOffset(IDiaSymbol *pType, LONG *pOff)
|
|
{
|
|
return pType->get_virtualBasePointerOffset(pOff);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetClassParentId(IDiaSymbol *pType, ULONG *pCid)
|
|
{
|
|
return pType->get_classParentId(pCid);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetNested(IDiaSymbol *pType, BOOL *pNested)
|
|
{
|
|
return pType->get_nested(pNested);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetSymAddress(IDiaSymbol *pType, ULONG64 ModBase, PULONG64 pAddr)
|
|
{
|
|
ULONG rva;
|
|
HRESULT Hr;
|
|
|
|
Hr = pType->get_relativeVirtualAddress(&rva);
|
|
if (Hr == S_OK) *pAddr = ModBase + rva;
|
|
return Hr;
|
|
}
|
|
|
|
HRESULT
|
|
diaGetThisAdjust(IDiaSymbol *pType, LONG *pThisAdjust)
|
|
{
|
|
return pType->get_thisAdjust(pThisAdjust);
|
|
}
|
|
|
|
HRESULT
|
|
diaGetUdtKind(IDiaSymbol *pType, DWORD *pUdtKind)
|
|
{
|
|
return pType->get_udtKind(pUdtKind);
|
|
}
|
|
|
|
BOOL
|
|
diaCompareTypeSym(
|
|
IN HANDLE hProcess,
|
|
IN DWORD64 ModBase,
|
|
IN IDiaSymbol *pType1,
|
|
IN OUT PULONG TypeId2
|
|
)
|
|
{
|
|
PPROCESS_ENTRY ProcessEntry;
|
|
PDIA pdia;
|
|
PMODULE_ENTRY mi;
|
|
CComPtr<IDiaSymbol> pType2;
|
|
|
|
ProcessEntry = FindProcessEntry( hProcess );
|
|
if (!ProcessEntry || !(mi = GetModFromAddr(ProcessEntry, ModBase))) {
|
|
return false;
|
|
}
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia) {
|
|
return false;
|
|
}
|
|
if (pdia->session->symbolById(*TypeId2, &pType2) != S_OK) {
|
|
return false;
|
|
}
|
|
if (pdia->session->symsAreEquiv(pType1, pType2) != S_OK) {
|
|
*TypeId2 = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
BOOL
|
|
diaFindTypeSym(
|
|
IN HANDLE hProcess,
|
|
IN DWORD64 ModBase,
|
|
IN ULONG TypeId,
|
|
OUT IDiaSymbol **pType
|
|
)
|
|
{
|
|
PPROCESS_ENTRY ProcessEntry;
|
|
PDIA pdia;
|
|
PMODULE_ENTRY mi;
|
|
|
|
ProcessEntry = FindProcessEntry( hProcess );
|
|
if (!ProcessEntry || !(mi = GetModFromAddr(ProcessEntry, ModBase))) {
|
|
return false;
|
|
}
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia) {
|
|
return false;
|
|
}
|
|
return pdia->session->symbolById(TypeId, pType) == S_OK;
|
|
}
|
|
|
|
#ifdef USE_CACHE
|
|
|
|
ULONG gHits=0, gLook=0;
|
|
|
|
void
|
|
diaInsertInCache(
|
|
PDIA_CACHE_ENTRY pCache,
|
|
PDIA_LARGE_DATA plVals,
|
|
ULONGLONG Module,
|
|
ULONG TypeId,
|
|
IMAGEHLP_SYMBOL_TYPE_INFO GetType,
|
|
PVOID pInfo
|
|
)
|
|
{
|
|
if (GetType == TI_IS_EQUIV_TO) {
|
|
// Not cached.
|
|
return;
|
|
}
|
|
|
|
int start = CACHE_BLOCK * (TypeId % CACHE_BLOCK);
|
|
int i, found;
|
|
ULONG len,age;
|
|
PDIA_LARGE_DATA pLargeVal=NULL;
|
|
|
|
if (GetType == TI_FINDCHILDREN || GetType == TI_GET_SYMNAME) {
|
|
for (pLargeVal = plVals, found=i=0, age=0; i<2*CACHE_BLOCK; i++) {
|
|
if (!plVals[i].Used) {
|
|
pLargeVal = &plVals[i];
|
|
break;
|
|
} else if (pCache[plVals[i].Index].Age > age) {
|
|
pLargeVal = &plVals[i];
|
|
age = pCache[plVals[i].Index].Age;
|
|
assert(DIACH_PLVAL == pCache[pLargeVal->Index].Data.type);
|
|
assert(pLargeVal == pCache[pLargeVal->Index].Data.plVal);
|
|
}
|
|
}
|
|
// } else {
|
|
// return;
|
|
}
|
|
// if (!(gLook % 200)) {
|
|
// if (GetType == TI_FINDCHILDREN || GetType == TI_GET_SYMNAME) {
|
|
// printf("Index \tUsed\tBy\t\tfound %lx\n", pLargeVal);
|
|
// for (found=i=0, age=0; i<2*CACHE_BLOCK; i++) {
|
|
// printf("%08lx \t%lx\t%lx\n",
|
|
// &plVals[i], plVals[i].Used, plVals[i].Index);
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
for (i=found=start, age=0; i<(start+CACHE_BLOCK); i++) {
|
|
if (++pCache[i].Age > age) {
|
|
age = pCache[i].Age; found = i;
|
|
}
|
|
}
|
|
i=found;
|
|
if (pCache[i].Data.type == DIACH_PLVAL) {
|
|
assert(pCache[i].Data.plVal->Index == (ULONG) i);
|
|
pCache[i].Data.plVal->Index = 0;
|
|
pCache[i].Data.plVal->Used = 0;
|
|
pCache[i].Data.type = 0;
|
|
pCache[i].Data.ullVal = 0;
|
|
}
|
|
pCache[i].Age = 0;
|
|
pCache[i].s.DataType = GetType;
|
|
pCache[i].s.TypeId = TypeId;
|
|
pCache[i].Module = Module;
|
|
|
|
switch (GetType) {
|
|
case TI_GET_SYMTAG:
|
|
case TI_GET_COUNT:
|
|
case TI_GET_CHILDRENCOUNT:
|
|
case TI_GET_BITPOSITION:
|
|
case TI_GET_VIRTUALBASECLASS:
|
|
case TI_GET_VIRTUALTABLESHAPEID:
|
|
case TI_GET_VIRTUALBASEPOINTEROFFSET:
|
|
case TI_GET_CLASSPARENTID:
|
|
case TI_GET_TYPEID:
|
|
case TI_GET_BASETYPE:
|
|
case TI_GET_ARRAYINDEXTYPEID:
|
|
case TI_GET_DATAKIND:
|
|
case TI_GET_ADDRESSOFFSET:
|
|
case TI_GET_OFFSET:
|
|
case TI_GET_NESTED:
|
|
case TI_GET_THISADJUST:
|
|
case TI_GET_UDTKIND:
|
|
pCache[i].Data.type = DIACH_ULVAL;
|
|
pCache[i].Data.ulVal = *((PULONG) pInfo);
|
|
break;
|
|
|
|
case TI_GET_LENGTH:
|
|
case TI_GET_ADDRESS:
|
|
pCache[i].Data.type = DIACH_ULLVAL;
|
|
pCache[i].Data.ullVal = *((PULONGLONG) pInfo);
|
|
break;
|
|
|
|
case TI_GET_SYMNAME: {
|
|
len = 2*(1+wcslen(*((BSTR *) pInfo)));
|
|
|
|
if (pLargeVal &&
|
|
len < sizeof(pLargeVal->Bytes)) {
|
|
// dtrace("Ins name %08lx %s had %3lx name %ws\n",
|
|
// pLargeVal, pLargeVal->Used ? "used" : "free",
|
|
// pLargeVal->Index, &pLargeVal->Bytes[0]);
|
|
memcpy(&pLargeVal->Bytes[0], *((BSTR *) pInfo), len);
|
|
pLargeVal->LengthUsed = len;
|
|
|
|
if (pLargeVal->Used) {
|
|
pCache[pLargeVal->Index].Data.type = 0;
|
|
pCache[pLargeVal->Index].Data.ullVal = 0;
|
|
pCache[pLargeVal->Index].SearchId = 0;
|
|
}
|
|
pCache[i].Data.type = DIACH_PLVAL;
|
|
pCache[i].Data.plVal = pLargeVal;
|
|
pLargeVal->Index = i;
|
|
pLargeVal->Used = true;
|
|
// dtrace(Ins %9I64lx ch %3lx lch %08lx name %ws\n",
|
|
// pCache[i].SearchId, i, pLargeVal, &pLargeVal->Bytes[0]);
|
|
} else {
|
|
pCache[i].SearchId = 0;
|
|
}
|
|
break;
|
|
}
|
|
case TI_FINDCHILDREN: {
|
|
TI_FINDCHILDREN_PARAMS *pChild = (TI_FINDCHILDREN_PARAMS *) pInfo;
|
|
|
|
len = sizeof(TI_FINDCHILDREN_PARAMS) + pChild->Count*sizeof(pChild->ChildId[0]) - sizeof(pChild->ChildId);
|
|
|
|
if (pLargeVal &&
|
|
len < sizeof(pLargeVal->Bytes)) {
|
|
// dtrace("Ins child %08lx %s had %3lx name %ws\n",
|
|
// pLargeVal, pLargeVal->Used ? "used" : "free",
|
|
// pLargeVal->Index, &pLargeVal->Bytes[0]);
|
|
memcpy(&pLargeVal->Bytes[0], pChild, len);
|
|
pLargeVal->LengthUsed = len;
|
|
if (pLargeVal->Used) {
|
|
pCache[pLargeVal->Index].Data.type = 0;
|
|
pCache[pLargeVal->Index].Data.ullVal = 0;
|
|
pCache[pLargeVal->Index].SearchId = 0;
|
|
}
|
|
pCache[i].Data.type = DIACH_PLVAL;
|
|
pCache[i].Data.plVal = pLargeVal;
|
|
pLargeVal->Index = i;
|
|
pLargeVal->Used = true;
|
|
} else {
|
|
pCache[i].SearchId = 0;
|
|
}
|
|
break;
|
|
}
|
|
case TI_GET_VALUE:
|
|
default:
|
|
pCache[i].Data.type = 0;
|
|
pCache[i].SearchId = 0;
|
|
return ;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
BOOL
|
|
diaLookupCache(
|
|
PDIA_CACHE_ENTRY pCache,
|
|
ULONG64 Module,
|
|
ULONG TypeId,
|
|
IMAGEHLP_SYMBOL_TYPE_INFO GetType,
|
|
PVOID pInfo
|
|
)
|
|
{
|
|
if (GetType == TI_IS_EQUIV_TO) {
|
|
// Not cached.
|
|
return false;
|
|
}
|
|
|
|
int start = CACHE_BLOCK * (TypeId % CACHE_BLOCK);
|
|
int i, found;
|
|
ULONGLONG Search = ((ULONGLONG) GetType << 32) + TypeId;
|
|
|
|
++gLook;
|
|
|
|
for (i=start,found=-1; i<(start+CACHE_BLOCK); i++) {
|
|
if (pCache[i].SearchId == Search &&
|
|
pCache[i].Module == Module) {
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
if (found == -1) {
|
|
return false;
|
|
}
|
|
|
|
i=found;
|
|
pCache[i].Age = 0;
|
|
switch (pCache[i].Data.type) {
|
|
case DIACH_ULVAL:
|
|
*((PULONG) pInfo) = pCache[i].Data.ulVal;
|
|
break;
|
|
|
|
case DIACH_ULLVAL:
|
|
*((PULONGLONG) pInfo) = pCache[i].Data.ullVal;
|
|
break;
|
|
|
|
case DIACH_PLVAL:
|
|
if (GetType == TI_GET_SYMNAME) {
|
|
|
|
*((BSTR *) pInfo) = (BSTR) LocalAlloc(0, pCache[i].Data.plVal->LengthUsed);
|
|
|
|
if (*((BSTR *) pInfo)) {
|
|
memcpy(*((BSTR *) pInfo), &pCache[i].Data.plVal->Bytes[0],pCache[i].Data.plVal->LengthUsed);
|
|
// dtrace(Lok %9I64lx ch %3lx lch %08lx name %ws\n",
|
|
// pCache[i].SearchId,
|
|
// i,
|
|
// pCache[i].Data.plVal,
|
|
// &pCache[i].Data.plVal->Bytes[0]);
|
|
}
|
|
} else if (GetType == TI_FINDCHILDREN) {
|
|
TI_FINDCHILDREN_PARAMS *pChild = (TI_FINDCHILDREN_PARAMS *) pInfo;
|
|
TI_FINDCHILDREN_PARAMS *pStored = (TI_FINDCHILDREN_PARAMS *) &pCache[i].Data.plVal->Bytes[0];
|
|
// dtrace(Lok %9I64lx ch %3lx lch %08lx child %lx\n",
|
|
// pCache[i].SearchId,
|
|
// i,
|
|
// pCache[i].Data.plVal,
|
|
// pStored->Count);
|
|
|
|
if (pChild->Count == pStored->Count &&
|
|
pChild->Start == pStored->Start) {
|
|
memcpy(pChild, pStored, pCache[i].Data.plVal->LengthUsed);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return false;
|
|
}
|
|
if (!(++gHits%50)) {
|
|
// dtrace("%ld %% Hits\n", (gHits * 100) / gLook);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#endif // USE_CACHE
|
|
|
|
HRESULT
|
|
#ifdef USE_CACHE
|
|
diaGetSymbolInfoEx(
|
|
#else
|
|
diaGetSymbolInfo(
|
|
#endif // USE_CACHE
|
|
IN HANDLE hProcess,
|
|
IN DWORD64 ModBase,
|
|
IN ULONG TypeId,
|
|
IN IMAGEHLP_SYMBOL_TYPE_INFO GetType,
|
|
OUT PVOID pInfo
|
|
)
|
|
{
|
|
assert(pInfo);
|
|
CComPtr <IDiaSymbol> pTypeSym;
|
|
if (!diaFindTypeSym(hProcess, ModBase, TypeId, &pTypeSym)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
switch (GetType) {
|
|
case TI_GET_SYMTAG:
|
|
return diaGetSymTag(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_SYMNAME:
|
|
return diaGetSymName(pTypeSym, (BSTR *) pInfo);
|
|
break;
|
|
case TI_GET_LENGTH:
|
|
return diaGetLength(pTypeSym, (PULONGLONG) pInfo);
|
|
break;
|
|
case TI_GET_TYPE:
|
|
case TI_GET_TYPEID:
|
|
return diaGetTypeId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_BASETYPE:
|
|
return diaGetBaseType(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_ARRAYINDEXTYPEID:
|
|
return diaGetArrayIndexTypeId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_FINDCHILDREN:
|
|
return diaFindChildren(pTypeSym, (TI_FINDCHILDREN_PARAMS *) pInfo);
|
|
case TI_GET_DATAKIND:
|
|
return diaGetDataKind(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_ADDRESSOFFSET:
|
|
return diaGetAddressOffset(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_OFFSET:
|
|
return diaGetOffset(pTypeSym, (PLONG) pInfo);
|
|
break;
|
|
case TI_GET_VALUE:
|
|
return diaGetValue(pTypeSym, (VARIANT *) pInfo);
|
|
break;
|
|
case TI_GET_COUNT:
|
|
return diaGetCount(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_CHILDRENCOUNT:
|
|
return diaGetChildrenCount(pTypeSym, (PLONG) pInfo);
|
|
break;
|
|
case TI_GET_BITPOSITION:
|
|
return diaGetBitPosition(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_VIRTUALBASECLASS:
|
|
return diaGetVirtualBaseClass(pTypeSym, (BOOL *) pInfo);
|
|
break;
|
|
case TI_GET_VIRTUALTABLESHAPEID:
|
|
return diaGetVirtualTableShapeId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_VIRTUALBASEPOINTEROFFSET:
|
|
return diaGetVirtualBasePointerOffset(pTypeSym, (PLONG) pInfo);
|
|
break;
|
|
case TI_GET_CLASSPARENTID:
|
|
return diaGetClassParentId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_NESTED:
|
|
return diaGetNested(pTypeSym, (PBOOL) pInfo);
|
|
break;
|
|
case TI_GET_SYMINDEX:
|
|
return diaGetSymIndexId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_LEXICALPARENT:
|
|
return diaGetLexicalParentId(pTypeSym, (PULONG) pInfo);
|
|
break;
|
|
case TI_GET_ADDRESS:
|
|
return diaGetSymAddress(pTypeSym, ModBase, (PULONG64) pInfo);
|
|
case TI_GET_THISADJUST:
|
|
return diaGetThisAdjust(pTypeSym, (PLONG) pInfo);
|
|
case TI_GET_UDTKIND:
|
|
return diaGetUdtKind(pTypeSym, (PDWORD) pInfo);
|
|
case TI_IS_EQUIV_TO:
|
|
if (!diaCompareTypeSym(hProcess, ModBase, pTypeSym, (PULONG)pInfo)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
return *(PULONG)pInfo != 0 ? S_OK : S_FALSE;
|
|
case TI_GET_CALLING_CONVENTION:
|
|
return diaGetCallingConvention(pTypeSym, (PULONG)pInfo);
|
|
break;
|
|
|
|
default:
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
#ifdef USE_CACHE
|
|
HRESULT
|
|
diaGetSymbolInfo(
|
|
IN HANDLE hProcess,
|
|
IN DWORD64 ModBase,
|
|
IN ULONG TypeId,
|
|
IN IMAGEHLP_SYMBOL_TYPE_INFO GetType,
|
|
OUT PVOID pInfo
|
|
)
|
|
{
|
|
PPROCESS_ENTRY ProcessEntry;
|
|
|
|
ProcessEntry = FindProcessEntry( hProcess );
|
|
|
|
if (!ProcessEntry) {
|
|
return E_INVALIDARG;
|
|
}
|
|
if (!diaLookupCache(ProcessEntry->DiaCache, ModBase, TypeId, GetType, pInfo)) {
|
|
HRESULT hr = diaGetSymbolInfoEx(hProcess, ModBase, TypeId, GetType, pInfo);
|
|
if (hr == S_OK) {
|
|
diaInsertInCache(ProcessEntry->DiaCache, ProcessEntry->DiaLargeData,
|
|
ModBase, TypeId, GetType, pInfo);
|
|
}
|
|
return hr;
|
|
}
|
|
return S_OK;
|
|
}
|
|
#endif // USE_CACHE
|
|
|
|
BOOL
|
|
diaGetTiForUDT(
|
|
PMODULE_ENTRY ModuleEntry,
|
|
LPSTR name,
|
|
PSYMBOL_INFO psi
|
|
)
|
|
{
|
|
BSTR wname=NULL;
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
ULONG celt;
|
|
|
|
if (!ModuleEntry) {
|
|
return false;
|
|
}
|
|
|
|
pdia = (PDIA)ModuleEntry->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbols;
|
|
hr = pdia->session->get_globalScope(&idiaSymbols);
|
|
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
if (name) {
|
|
wname = AnsiToUnicode(name);
|
|
}
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaEnum;
|
|
|
|
hr = idiaSymbols->findChildren(SymTagNull, wname, nsCaseSensitive, &idiaEnum);
|
|
if (hr == S_OK) {
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol;
|
|
|
|
if ((hr = idiaEnum->Next( 1, &idiaSymbol, &celt)) == S_OK && celt == 1) {
|
|
diaFillSymbolInfo(psi, ModuleEntry, idiaSymbol);
|
|
idiaSymbol->get_symIndexId(&psi->TypeIndex);
|
|
idiaSymbol = NULL;
|
|
}
|
|
}
|
|
|
|
MemFree(wname);
|
|
|
|
return hr == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
diaEnumUDT(
|
|
PMODULE_ENTRY ModuleEntry,
|
|
LPSTR name,
|
|
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
|
|
PVOID context
|
|
)
|
|
{
|
|
BSTR wname=NULL;
|
|
PDIA pdia;
|
|
HRESULT hr;
|
|
ULONG celt;
|
|
CHAR buff[MAX_SYM_NAME + sizeof(SYMBOL_INFO)];
|
|
PSYMBOL_INFO psi=(PSYMBOL_INFO)buff;
|
|
BOOL rc;
|
|
|
|
psi->MaxNameLen = MAX_SYM_NAME;
|
|
|
|
if (!ModuleEntry) {
|
|
return false;
|
|
}
|
|
|
|
pdia = (PDIA)ModuleEntry->dia;
|
|
if (!pdia)
|
|
return false;
|
|
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbols;
|
|
hr = pdia->session->get_globalScope(&idiaSymbols);
|
|
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
if (name && *name) {
|
|
wname = AnsiToUnicode(name);
|
|
}
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaEnum;
|
|
|
|
hr = idiaSymbols->findChildren(SymTagNull, wname, nsCaseSensitive, &idiaEnum);
|
|
if (hr == S_OK) {
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol;
|
|
|
|
while (SUCCEEDED(idiaEnum->Next( 1, &idiaSymbol, &celt)) && celt == 1) {
|
|
ULONG tag;
|
|
idiaSymbol->get_symTag(&tag);
|
|
switch (tag)
|
|
{
|
|
case SymTagEnum:
|
|
case SymTagTypedef:
|
|
case SymTagUDT:
|
|
if (EnumSymbolsCallback) {
|
|
diaFillSymbolInfo(psi, ModuleEntry, idiaSymbol);
|
|
idiaSymbol->get_symIndexId(&psi->TypeIndex);
|
|
rc = EnumSymbolsCallback(psi, 0, context);
|
|
if (!rc)
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
idiaSymbol = NULL;
|
|
}
|
|
}
|
|
|
|
MemFree(wname);
|
|
|
|
return hr == S_OK;
|
|
}
|
|
|
|
BOOL
|
|
ldiaGetFrameData(
|
|
IN HANDLE Process,
|
|
IN ULONGLONG Offset,
|
|
OUT IDiaFrameData** FrameData
|
|
)
|
|
{
|
|
PPROCESS_ENTRY ProcessEntry;
|
|
PDIA Dia;
|
|
PMODULE_ENTRY Mod;
|
|
|
|
ProcessEntry = FindProcessEntry(Process);
|
|
if (!ProcessEntry ||
|
|
!(Mod = GetModFromAddr(ProcessEntry, Offset)) ||
|
|
!(Dia = (PDIA)Mod->dia)) {
|
|
return false;
|
|
}
|
|
|
|
if (Dia->framedata == NULL) {
|
|
|
|
CComPtr<IDiaEnumTables> EnumTables;
|
|
CComPtr<IDiaTable> FdTable;
|
|
VARIANT FdVar;
|
|
|
|
FdVar.vt = VT_BSTR;
|
|
FdVar.bstrVal = DiaTable_FrameData;
|
|
|
|
if (Dia->session->getEnumTables(&EnumTables) != S_OK ||
|
|
EnumTables->Item(FdVar, &FdTable) != S_OK ||
|
|
FdTable->QueryInterface(IID_IDiaEnumFrameData,
|
|
(void**)&Dia->framedata) != S_OK) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return Dia->framedata->frameByVA(Offset, FrameData) == S_OK;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaGetFrameData(
|
|
IN HANDLE Process,
|
|
IN ULONGLONG Offset,
|
|
OUT IDiaFrameData** FrameData
|
|
)
|
|
{
|
|
BOOL rc = false;
|
|
|
|
__try {
|
|
|
|
EnterCriticalSection(&g.threadlock);
|
|
rc = ldiaGetFrameData(Process, Offset,FrameData);
|
|
|
|
} __finally {
|
|
|
|
LeaveCriticalSection(&g.threadlock);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------
|
|
// for compatibility with GetFileLineOffsets. DON'T CALL THIS CODE!
|
|
|
|
#if 1
|
|
HRESULT
|
|
diaAddLinesForSourceFile(
|
|
PMODULE_ENTRY mi,
|
|
IDiaSourceFile *idiaSource,
|
|
IDiaSymbol *pComp
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
LPSTR SrcFileName = NULL;
|
|
BSTR wfname = NULL;
|
|
ULONG SrcFileNameLen = 0;
|
|
PSOURCE_ENTRY Src;
|
|
PSOURCE_ENTRY Seg0Src;
|
|
PSOURCE_LINE SrcLine;
|
|
PDIA pdia;
|
|
ULONG celt;
|
|
LONG LineNums;
|
|
ULONG CompId;
|
|
CHAR fname[MAX_PATH + 1];
|
|
DWORD rva;
|
|
ULONG Line;
|
|
|
|
if (!idiaSource) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
assert((mi != NULL) && (mi->dia));
|
|
|
|
pdia = (PDIA)mi->dia;
|
|
|
|
if (pComp->get_symIndexId(&CompId) == S_OK) {
|
|
}
|
|
|
|
CComPtr <IDiaEnumLineNumbers> idiaEnumLines;
|
|
|
|
hr = pdia->session->findLines(pComp, idiaSource, &idiaEnumLines);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
hr = idiaEnumLines->get_Count(&LineNums);
|
|
if (hr != S_OK)
|
|
return hr;
|
|
|
|
CComPtr <IDiaLineNumber> idiaLine;
|
|
|
|
if (idiaSource->get_fileName(&wfname) == S_OK && wfname) {
|
|
wcs2ansi(wfname, fname, MAX_PATH);
|
|
LocalFree(wfname);
|
|
SrcFileNameLen = strlen(fname);
|
|
}
|
|
|
|
Src = (PSOURCE_ENTRY)MemAlloc(sizeof(SOURCE_ENTRY)+
|
|
sizeof(SOURCE_LINE)*LineNums+
|
|
SrcFileNameLen + 1);
|
|
|
|
if (!Src) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
#ifdef DBG_DIA_LINE
|
|
dtrace("diaAddLinesForSourceFile : source : %s\n", fname);
|
|
#endif
|
|
|
|
// Retrieve line numbers and offsets from raw data and
|
|
// process them into current pointers.
|
|
|
|
SrcLine = (SOURCE_LINE *)(Src+1);
|
|
Src->LineInfo = SrcLine;
|
|
Src->ModuleId = CompId;
|
|
Src->MaxAddr = 0;
|
|
Src->MinAddr = -1;
|
|
|
|
Src->Lines = 0;
|
|
idiaLine = NULL;
|
|
for (; (hr = idiaEnumLines->Next(1, &idiaLine, &celt)) == S_OK && (celt == 1); ) {
|
|
hr = idiaLine->get_lineNumber(&Line);
|
|
if (hr != S_OK)
|
|
break;
|
|
hr = idiaLine->get_relativeVirtualAddress(&rva);
|
|
if (hr != S_OK)
|
|
break;
|
|
|
|
|
|
SrcLine->Line = Line;
|
|
SrcLine->Addr = GetLineAddressFromRva(mi, rva);
|
|
|
|
if (SrcLine->Addr > Src->MaxAddr) {
|
|
Src->MaxAddr = SrcLine->Addr;
|
|
}
|
|
if (SrcLine->Addr < Src->MinAddr) {
|
|
Src->MinAddr = SrcLine->Addr;
|
|
}
|
|
#ifdef DBG_DIA_LINE
|
|
dtrace("Add line %lx, Addr %I64lx\n", SrcLine->Line, SrcLine->Addr);
|
|
#endif
|
|
|
|
Src->Lines++;
|
|
SrcLine++;
|
|
idiaLine = NULL;
|
|
}
|
|
|
|
// Stick file name at the very end of the data block so
|
|
// it doesn't interfere with alignment.
|
|
Src->File = (LPSTR)SrcLine;
|
|
if (*fname) {
|
|
memcpy(Src->File, fname, SrcFileNameLen);
|
|
}
|
|
Src->File[SrcFileNameLen] = 0;
|
|
|
|
AddSourceEntry(mi, Src);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaAddLinesForMod(
|
|
PMODULE_ENTRY mi,
|
|
IDiaSymbol *diaModule
|
|
)
|
|
{
|
|
LONG Size;
|
|
BOOL Ret;
|
|
PSOURCE_ENTRY Src;
|
|
ULONG ModId;
|
|
HRESULT Hr;
|
|
|
|
if (diaModule->get_symIndexId(&ModId) != S_OK) {
|
|
return false;
|
|
}
|
|
#ifdef DBG_DIA_LINE
|
|
dtrace("diaAddLinesForMod : ModId %lx\n", ModId);
|
|
#endif
|
|
|
|
// Check and see if we've loaded this information already.
|
|
for (Src = mi->SourceFiles; Src != NULL; Src = Src->Next) {
|
|
// Check module index instead of pointer since there's
|
|
// no guarantee the pointer would be the same for different
|
|
// lookups.
|
|
if (Src->ModuleId == ModId) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
PDIA pdia;
|
|
pdia = (PDIA)mi->dia;
|
|
|
|
CComPtr< IDiaEnumSourceFiles > idiaEnumFiles;
|
|
Hr = pdia->session->findFile(diaModule, NULL, nsNone, &idiaEnumFiles);
|
|
if (Hr != S_OK) {
|
|
return false;
|
|
}
|
|
|
|
ULONG celt;
|
|
CComPtr <IDiaSourceFile> idiaSource;
|
|
for (;SUCCEEDED(idiaEnumFiles->Next(1,&idiaSource, &celt)) && (celt == 1);) {
|
|
diaAddLinesForSourceFile(mi, idiaSource, diaModule);
|
|
idiaSource = NULL;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
BOOL
|
|
diaAddLinesForModAtAddr(
|
|
PMODULE_ENTRY mi,
|
|
DWORD64 Addr
|
|
)
|
|
{
|
|
BOOL Ret;
|
|
DWORD Bias;
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
DWORD rva;
|
|
|
|
assert(mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
rva = (DWORD)(Addr - mi->BaseOfDll);
|
|
|
|
CComPtr < IDiaSymbol > pComp;
|
|
hr = pdia->session->findSymbolByRVA(rva, SymTagCompiland, &pComp);
|
|
if (hr != S_OK)
|
|
return false;
|
|
|
|
Ret = diaAddLinesForMod(mi, pComp);
|
|
|
|
return Ret;
|
|
}
|
|
|
|
BOOL
|
|
diaAddLinesForAllMod(
|
|
PMODULE_ENTRY mi
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
PDIA pdia;
|
|
ULONG celt = 1;
|
|
BOOL Ret;
|
|
|
|
Ret = false;
|
|
#ifdef DBG_DIA_LINE
|
|
dtrace("diaAddLinesForAllMod : Adding lines for all mods in %s\n", mi->ImageName);
|
|
#endif
|
|
|
|
assert(mi && mi->dia);
|
|
pdia = (PDIA)mi->dia;
|
|
if (!pdia)
|
|
return NULL;
|
|
|
|
CComPtr <IDiaSymbol> idiaSymbols;
|
|
|
|
hr = pdia->session->get_globalScope(&idiaSymbols);
|
|
if (hr != S_OK)
|
|
return NULL;
|
|
|
|
CComPtr< IDiaEnumSymbols > idiaMods;
|
|
hr = pdia->session->findChildren(idiaSymbols,SymTagCompiland, NULL, nsNone, &idiaMods);
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
CComPtr< IDiaSymbol > idiaSymbol;
|
|
|
|
while (SUCCEEDED(idiaMods->Next( 1, &idiaSymbol, &celt)) && celt == 1) {
|
|
Ret = diaAddLinesForMod(mi, idiaSymbol);
|
|
idiaSymbol = NULL;
|
|
if (!Ret) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
#endif
|
|
|
|
|