Files
sigmaker-ida/idasdk76/plugins/pdb/pdblocal.cpp
2021-10-31 21:20:46 +02:00

400 lines
10 KiB
C++

//----------------------------------------------------------------------------
template <typename T>
struct dia_ptr_t
{
dia_ptr_t() : thing(NULL) {}
~dia_ptr_t()
{
if ( thing != NULL )
thing->Release();
}
T *thing;
};
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::_do_iterate_symbols_enumerator(
IDiaEnumSymbols *sym_enum,
children_visitor_t &visitor)
{
std::set<DWORD> seen;
HRESULT hr = S_OK;
while ( true )
{
ULONG celt = 0;
IDiaSymbol *pChild = NULL;
hr = sym_enum->Next(1, &pChild, &celt);
if ( FAILED(hr) || celt != 1 )
{
hr = S_OK; // end of enumeration
break;
}
pdb_sym_t *child = create_sym(pChild, true);
pdb_sym_janitor_t janitor_pType(child);
DWORD sym_id;
hr = child->get_symIndexId(&sym_id);
if ( hr != S_OK )
break;
// It seems we can, in some cases, iterate over the
// same child more than once.
// Fortunately, it appears to be the same symbol data;
// and not another symbol w/ the same ID
// See also: sip_iterate_symbols_at_ea()
if ( seen.insert(sym_id).second )
{
hr = visitor.visit_child(*child);
if ( FAILED(hr) )
break;
}
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::safe_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor)
{
HRESULT hr = E_FAIL;
IDiaEnumSymbols *pEnumSymbols;
try
{
QASSERT(30536, sym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &diasym = (dia_pdb_sym_t &)sym;
hr = dia_session->findChildren(diasym.data, type, NULL, nsNone, &pEnumSymbols);
if ( hr == S_OK )
{
hr = _do_iterate_symbols_enumerator(pEnumSymbols, visitor);
pEnumSymbols->Release();
}
}
catch ( const std::bad_alloc & )
{
// try to free some memory before quitting (and saving the idb)
delete this;
nomem("pdb");
}
catch ( const std::exception &e )
{
error("Unhandled C++ exception: %s", e.what());
}
catch ( ... )
{
error("Unhandled C++ exception!");
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::do_iterate_children(
pdb_sym_t &sym,
enum SymTagEnum type,
children_visitor_t &visitor)
{
int code;
HRESULT hr = E_FAIL;
__try
{
hr = safe_iterate_children(sym, type, visitor);
}
__except ( code=GetExceptionCode(), EXCEPTION_EXECUTE_HANDLER )
{
// complain to the user
ask_for_feedback(
"%s: %s\n"
"Is the corresponding PDB file valid?",
pdbargs.input_path.c_str(),
winerr(code));
// we may arrive here because we ran out of memory
// try to free some memory before quitting (and saving the idb)
delete this;
error(NULL); // and die... this will save the idb
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT local_pdb_access_t::load(pdb_sym_t &pdbsym, DWORD id)
{
IDiaSymbol *dia_sym;
HRESULT hr = dia_session->symbolById(id, &dia_sym);
if ( hr == S_OK )
{
QASSERT(30543, pdbsym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &sym = (dia_pdb_sym_t &)pdbsym;
sym.set_symbol_data(dia_sym, true);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::_copy_line_numbers(
pdb_lnnums_t *out,
IDiaEnumLineNumbers *enumerator) const
{
LONG count = 0;
HRESULT hr = enumerator->get_Count(&count);
if ( hr == S_OK )
{
IDiaLineNumber *lines[64];
ULONG got = 0;
for ( LONG i=0; i < count; i += got )
{
// Fetch many line number information at once
enumerator->Next(qnumber(lines), lines, &got);
if ( got == 0 )
break;
for ( ULONG j=0; j < got; j++ )
{
IDiaLineNumber *l = lines[j];
pdb_lnnum_t &lo = out->push_back();
l->get_virtualAddress(&lo.va);
l->get_length(&lo.length);
l->get_columnNumber(&lo.columnNumber);
l->get_columnNumberEnd(&lo.columnNumberEnd);
l->get_lineNumber(&lo.lineNumber);
l->get_lineNumberEnd(&lo.lineNumberEnd);
l->get_statement(&lo.statement);
IDiaSourceFile *f = NULL;
if ( l->get_sourceFile(&f) == S_OK )
{
f->get_uniqueId(&lo.file_id);
f->Release();
}
lines[j]->Release();
}
}
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_lines_by_va(
pdb_lnnums_t *out,
ULONGLONG va,
ULONGLONG length)
{
dia_ptr_t<IDiaEnumLineNumbers> pEnumLineNumbers;
HRESULT hr = dia_session->findLinesByVA(va, length, &pEnumLineNumbers.thing);
if ( hr == S_OK )
hr = _copy_line_numbers(out, pEnumLineNumbers.thing);
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_lines_by_coords(
pdb_lnnums_t *out,
DWORD file_id,
int lnnum,
int colnum)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( FAILED(hr) )
return hr;
dia_ptr_t<IDiaEnumSymbols> pEnumSymbols;
hr = pFile.thing->get_compilands(&pEnumSymbols.thing);
if ( FAILED(hr) )
return hr;
while ( true )
{
ULONG got = 0;
IDiaSymbol *compiland;
pEnumSymbols.thing->Next(1, &compiland, &got);
if ( got == 0 )
break;
dia_ptr_t<IDiaEnumLineNumbers> pEnumLineNumbers;
HRESULT hr2;
if ( lnnum == 0 )
hr2 = dia_session->findLines(
compiland,
pFile.thing,
&pEnumLineNumbers.thing);
else
hr2 = dia_session->findLinesByLinenum(
compiland,
pFile.thing,
lnnum,
colnum,
&pEnumLineNumbers.thing);
compiland->Release();
if ( hr == S_OK )
_copy_line_numbers(out, pEnumLineNumbers.thing);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_iterate_symbols_at_ea(
ULONGLONG va,
ULONGLONG size,
enum SymTagEnum tag,
children_visitor_t &visitor)
{
// See also: _do_iterate_symbols_enumerator
std::set<DWORD> seen;
ea_t cur = va;
while ( true )
{
if ( cur >= va + size )
break;
ea_t old = cur;
qnotused(old);
LONG disp;
IDiaSymbol *sym = NULL;
HRESULT hr = dia_session->findSymbolByVAEx(cur, tag, &sym, &disp);
if ( FAILED(hr) || sym == NULL )
break;
// perform all get_*'s on 'sym' _before_ the visitor is called: it might
// very well 'steal' the symbol & destroy it in case it's not needed.
// (see source_items_vec_builder_t::visit_child())
pdb_sym_t *psym = create_sym(sym, true);
pdb_sym_janitor_t janitor_psym(psym);
DWORD sym_id;
hr = psym->get_symIndexId(&sym_id);
if ( hr != S_OK )
break;
ULONGLONG length = 0;
sym->get_length(&length);
if ( seen.insert(sym_id).second )
{
hr = visitor.visit_child(*psym);
if ( FAILED(hr) )
break;
}
cur -= disp;
cur += length;
QASSERT(30169, cur > old); // to avoid endless loops - i do not know if they are possible
}
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_iterate_file_compilands(
DWORD file_id,
children_visitor_t &visitor)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( FAILED(hr) )
return hr;
dia_ptr_t<IDiaEnumSymbols> pEnumSymbols;
hr = pFile.thing->get_compilands(&pEnumSymbols.thing);
if ( hr == S_OK )
hr = _do_iterate_symbols_enumerator(pEnumSymbols.thing, visitor);
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_file_path(
qstring *out,
qstring *errbuf,
DWORD file_id)
{
dia_ptr_t<IDiaSourceFile> pFile;
HRESULT hr = dia_session->findFileById(file_id, &pFile.thing);
if ( hr == S_OK )
{
BSTR path;
hr = pFile.thing->get_fileName(&path);
if ( hr == S_OK )
{
utf16_utf8(out, path);
SysFreeString(path);
}
}
if ( FAILED(hr) )
{
if ( errbuf != NULL )
*errbuf = winerr(hr);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::_copy_files_ids(
qvector<DWORD> *out,
IDiaEnumSourceFiles *enumerator) const
{
ULONG celt = 0;
IDiaSourceFile *file = NULL;
while ( enumerator->Next(1, &file, &celt) == S_OK && celt > 0 )
{
DWORD file_id;
if ( file->get_uniqueId(&file_id) == S_OK )
out->push_back(file_id);
file->Release();
}
return S_OK;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_retrieve_symbol_files(
qvector<DWORD> *out,
pdb_sym_t &sym)
{
// Retrieve source file name associated with the current symbol
QASSERT(30537, sym.whoami() == DIA_PDB_SYM);
dia_pdb_sym_t &diasym = (dia_pdb_sym_t &)sym;
BSTR path;
HRESULT hr = diasym.data->get_sourceFileName(&path);
if ( hr == S_OK ) // cannot use SUCCEEDED(hr) because S_OK means success
{
dia_ptr_t<IDiaEnumSourceFiles> pEnumSourceFiles;
hr = dia_session->findFile(NULL, path, nsfFNameExt, &pEnumSourceFiles.thing);
SysFreeString(path);
if ( hr == S_OK )
_copy_files_ids(out, pEnumSourceFiles.thing);
}
return hr;
}
//-------------------------------------------------------------------------
HRESULT local_pdb_access_t::sip_find_files(
qvector<DWORD> *out,
const char *filename)
{
qwstring fnamebuf;
wchar16_t *fname = NULL;
if ( filename != NULL )
{
qstring fnametmp = filename;
utf8_utf16(&fnamebuf, &fnametmp[0]);
fname = fnamebuf.begin();
}
dia_ptr_t<IDiaEnumSourceFiles> pEnumSourceFiles;
HRESULT hr = dia_session->findFile(
NULL,
fname,
nsfFNameExt | nsfCaseInsensitive,
&pEnumSourceFiles.thing);
if ( hr == S_OK )
_copy_files_ids(out, pEnumSourceFiles.thing);
return hr;
}