1295 lines
35 KiB
C++
1295 lines
35 KiB
C++
|
|
#include "sip.hpp"
|
|
#include "tilbuild.hpp"
|
|
|
|
#include <dbg.hpp>
|
|
|
|
struct pdb_modinfo_t;
|
|
|
|
static source_item_t *new_pdb_symbol(
|
|
pdb_modinfo_t *pdb_module,
|
|
DWORD sym_id);
|
|
static source_item_t *new_pdb_symbol_or_delete(
|
|
pdb_modinfo_t *pdb_module,
|
|
pdb_sym_t *sym);
|
|
|
|
typedef qvector<source_item_ptr> source_items_vec_t;
|
|
|
|
//-------------------------------------------------------------------------
|
|
// will steal data from all 'child' instances passed to 'visit_child()',
|
|
// and create new pdb_sym_t objects (wrapped in source_item_t's) with
|
|
// that data.
|
|
struct source_items_vec_builder_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
source_items_vec_t items;
|
|
pdb_modinfo_t *pdb_module;
|
|
source_items_vec_builder_t(pdb_modinfo_t *_mod) : pdb_module(_mod) {}
|
|
virtual HRESULT visit_child(pdb_sym_t &child) override;
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Implementation of source information provider using PDB information
|
|
// and the DIA SDK
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Information about a PDB module. Contains a type cache, that'll
|
|
// last the same time as the debugging session.
|
|
struct pdb_modinfo_t
|
|
{
|
|
pdb_ctx_t &pv;
|
|
|
|
pdb_modinfo_t()
|
|
: pv(*GET_MODULE_DATA(pdb_ctx_t)),
|
|
base(BADADDR),
|
|
size(0),
|
|
opened(false),
|
|
pdb_access(NULL),
|
|
type_cache(NULL),
|
|
use_pdbida(false)
|
|
{}
|
|
|
|
~pdb_modinfo_t()
|
|
{
|
|
delete type_cache;
|
|
#ifdef __NT__
|
|
// on windows with MSDIA provider, 'session_ref' owns the pdb_access
|
|
if ( use_pdbida )
|
|
#endif
|
|
{
|
|
delete pdb_access;
|
|
}
|
|
}
|
|
|
|
pdb_access_t *get_access() { return pdb_access; }
|
|
|
|
HRESULT open(const char *input_file, const char *user_spath, ea_t load_address);
|
|
source_item_t *find_static_item_in_module(const char *iname);
|
|
|
|
typedef std::map<qstring,DWORD> module_globals_t;
|
|
module_globals_t module_globals;
|
|
|
|
qstring path;
|
|
ea_t base;
|
|
asize_t size;
|
|
bool opened;
|
|
pdb_access_t *pdb_access;
|
|
#ifndef ENABLE_REMOTEPDB
|
|
pdb_session_ref_t session_ref;
|
|
#endif
|
|
til_builder_t *type_cache;
|
|
bool use_pdbida;
|
|
};
|
|
typedef std::map<ea_t, pdb_modinfo_t> pdb_modules_t;
|
|
|
|
//-------------------------------------------------------------------------
|
|
HRESULT pdb_modinfo_t::open(
|
|
const char *input_file,
|
|
const char *user_spath,
|
|
ea_t load_address)
|
|
{
|
|
QASSERT(30212, type_cache == NULL);
|
|
pdbargs_t args;
|
|
args.input_path = input_file;
|
|
args.spath = user_spath;
|
|
args.loaded_base = load_address;
|
|
HRESULT hr = E_FAIL;
|
|
{
|
|
msg("PDB: using MSDIA provider\n");
|
|
#ifdef ENABLE_REMOTEPDB
|
|
if ( is_win32_remote_debugger_loaded() )
|
|
{
|
|
args.flags |= PDBFLG_DBG_MODULE;
|
|
remote_pdb_access_t *rpdb_access = new remote_pdb_access_t(
|
|
args,
|
|
"TESTER-SQUISH",
|
|
23946,
|
|
"");
|
|
hr = rpdb_access->open_connection();
|
|
if ( hr == S_OK )
|
|
pdb_access = rpdb_access;
|
|
else
|
|
delete rpdb_access;
|
|
}
|
|
#else // ENABLE_REMOTEPDB
|
|
hr = session_ref.open_session(args);
|
|
if ( hr == S_OK )
|
|
pdb_access = session_ref.session->pdb_access;
|
|
#endif // ENABLE_REMOTEPDB
|
|
}
|
|
if ( hr == S_OK )
|
|
{
|
|
type_cache = new til_builder_t(pv, CONST_CAST(til_t *)(get_idati()), NULL);
|
|
type_cache->set_pdb_access(pdb_access);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
source_item_t *pdb_modinfo_t::find_static_item_in_module(
|
|
const char *iname)
|
|
{
|
|
if ( !opened )
|
|
return NULL;
|
|
|
|
DWORD id = 0;
|
|
module_globals_t::iterator p = module_globals.find(iname);
|
|
if ( p != module_globals.end() )
|
|
{
|
|
id = p->second;
|
|
}
|
|
else
|
|
{
|
|
struct ida_local iter_t : public pdb_access_t::children_visitor_t
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
const char *name;
|
|
DWORD id;
|
|
iter_t(
|
|
pdb_modinfo_t *_pdb_module,
|
|
const char *_name)
|
|
: pdb_module(_pdb_module), name(_name), id(0) {}
|
|
virtual HRESULT visit_child(pdb_sym_t &data) override
|
|
{
|
|
qstring cname;
|
|
if ( data.get_name(&cname) == S_OK && cname == name )
|
|
data.get_symIndexId(&id);
|
|
return id == 0 ? S_OK : E_FAIL; // 'id' still 0? keep iterating
|
|
}
|
|
};
|
|
iter_t iter(this, iname);
|
|
pdb_sym_t *global = get_access()->create_sym(get_access()->get_global_symbol_id());
|
|
pdb_sym_janitor_t janitor_global(global);
|
|
get_access()->iterate_children(*global, SymTagData, iter);
|
|
id = iter.id;
|
|
module_globals[iname] = id; // populate
|
|
}
|
|
return id != 0 ? new_pdb_symbol(this, id) : NULL;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
bool pdb_ctx_t::get_pdb_register_info(int *p_reg, uint64 *p_mask, int machine, int reg)
|
|
{
|
|
qstring name;
|
|
print_pdb_register(&name, machine, reg);
|
|
reg_info_t ri;
|
|
if ( !parse_reg_name(&ri, name.c_str()) )
|
|
return false;
|
|
*p_reg = ri.reg;
|
|
*p_mask = left_shift(uint64(1), 8*ri.size) - 1;
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct pdb_source_file_t : public source_file_t
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
DWORD file_id;
|
|
qstring my_path;
|
|
|
|
pdb_source_file_t(pdb_modinfo_t *_mod, DWORD _fid)
|
|
: pdb_module(_mod), file_id(_fid) {}
|
|
|
|
srcinfo_provider_t *idaapi get_provider(void) const override;
|
|
virtual ~pdb_source_file_t(void) {}
|
|
virtual void idaapi release() override { delete this; }
|
|
const char *idaapi get_path(qstring *errbuf) override
|
|
{
|
|
if ( my_path.empty() )
|
|
{
|
|
pdb_module->get_access()->sip_retrieve_file_path(
|
|
&my_path,
|
|
errbuf,
|
|
file_id);
|
|
}
|
|
return my_path.c_str();
|
|
}
|
|
|
|
bool idaapi read_file(strvec_t *buf, qstring *errbuf) override
|
|
{
|
|
buf->clear();
|
|
const char *path = get_path(errbuf);
|
|
|
|
// Always favor file mapping first.
|
|
qstring mapbuf = path;
|
|
callui(ui_dbg_map_source_path, &mapbuf);
|
|
path = mapbuf.c_str();
|
|
|
|
if ( !qfileexist(path) )
|
|
{
|
|
if ( errbuf != NULL )
|
|
errbuf->sprnt("source file not found: %s", path);
|
|
return false;
|
|
}
|
|
|
|
FILE *fp = fopenRT(path);
|
|
if ( fp == NULL )
|
|
{
|
|
if ( errbuf != NULL )
|
|
*errbuf = get_errdesc(path);
|
|
return false;
|
|
}
|
|
|
|
int tabsize = get_tab_size(path);
|
|
qstring line;
|
|
while ( qgetline(&line, fp) >= 0 )
|
|
{
|
|
simpleline_t &sl = buf->push_back();
|
|
sl.line.clear();
|
|
replace_tabs(&sl.line, line.c_str(), tabsize);
|
|
}
|
|
|
|
qfclose(fp);
|
|
return true;
|
|
}
|
|
|
|
TWidget *open_srcview(strvec_t ** /*strvec*/, TWidget ** /*pview*/, int, int) override
|
|
{
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
struct pdb_file_iterator : public _source_file_iterator
|
|
{
|
|
struct entry_t
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
DWORD file_id;
|
|
};
|
|
qvector<entry_t> entries;
|
|
int idx;
|
|
|
|
pdb_file_iterator() : idx(-1) {}
|
|
virtual ~pdb_file_iterator(void) {}
|
|
|
|
virtual void idaapi release(void) override { delete this; }
|
|
bool idaapi first(void) override
|
|
{
|
|
idx = -1;
|
|
return next();
|
|
}
|
|
|
|
bool idaapi next(void) override
|
|
{
|
|
++idx;
|
|
return idx < entries.size();
|
|
}
|
|
|
|
source_file_ptr idaapi operator *() override
|
|
{
|
|
const entry_t &e = entries[idx];
|
|
return source_file_ptr(
|
|
new pdb_source_file_t(e.pdb_module, e.file_id));
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Dummy source item: provides no information.
|
|
struct dummy_item_t : public source_item_t
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
|
|
dummy_item_t(pdb_modinfo_t *_pdb_module) : pdb_module(_pdb_module) {}
|
|
virtual ~dummy_item_t(void) { pdb_module = nullptr; }
|
|
void idaapi release(void) override { delete this; }
|
|
source_file_iterator idaapi get_source_files(void) override { return source_file_iterator(NULL); }
|
|
int idaapi get_lnnum() const override { return -1; }
|
|
int idaapi get_end_lnnum() const override { return -1; }
|
|
int idaapi get_colnum() const override { return -1; }
|
|
int idaapi get_end_colnum() const override { return -1; }
|
|
ea_t idaapi get_ea() const override { return BADADDR; }
|
|
asize_t idaapi get_size() const override { return 0; }
|
|
bool idaapi get_item_bounds(rangeset_t *set) const override
|
|
{
|
|
ea_t ea = get_ea();
|
|
if ( ea == BADADDR )
|
|
return false;
|
|
asize_t size = get_size(); //-V779 Unreachable code detected
|
|
set->add(range_t(ea, ea+size));
|
|
return true;
|
|
}
|
|
source_item_ptr idaapi get_parent(src_item_kind_t) const override { return source_item_ptr(NULL); }
|
|
source_item_iterator idaapi create_children_iterator() override { return source_item_iterator(NULL); }
|
|
bool idaapi get_hint(qstring *hint, const eval_ctx_t *, int *nlines) const override
|
|
{
|
|
// TODO: remove these test lines
|
|
*hint = "test";
|
|
*nlines = 1;
|
|
return true;
|
|
}
|
|
bool idaapi evaluate(const eval_ctx_t *, idc_value_t *, qstring *) const override { return false; }
|
|
// bool idaapi get_stkvar_info(char *, size_t, uval_t *, ea_t) const { return false; }
|
|
// bool idaapi get_regvar_info(char *, size_t) const { return false; }
|
|
// bool idaapi get_rrlvar_info(char *, size_t, uval_t *) const { return false; }
|
|
bool idaapi get_expr_tinfo(tinfo_t *) const override { return false; }
|
|
|
|
virtual bool idaapi get_location(argloc_t *, const eval_ctx_t *) const override { return false; }
|
|
|
|
virtual srcinfo_provider_t *idaapi get_provider(void) const override;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
bool pdb_lnnums_t::get_item_bounds(rangeset_t *set) const
|
|
{
|
|
for ( size_t i = 0, sz = size(); i < sz; ++i )
|
|
{
|
|
const pdb_lnnum_t &ln = at(i);
|
|
set->add(ln.va, ln.va + ln.length);
|
|
}
|
|
return !set->empty();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int pdb_lnnums_t::get_lnnum() const
|
|
{
|
|
return empty() ? -1 : at(0).lineNumber;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int pdb_lnnums_t::get_colnum() const
|
|
{
|
|
return empty() ? -1 : at(0).columnNumber;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int pdb_lnnums_t::get_end_lnnum() const
|
|
{
|
|
return empty() ? -1 : at(size() - 1).lineNumber; //should it be lineNumberEnd; ?
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
int pdb_lnnums_t::get_end_colnum() const
|
|
{
|
|
return empty() ? -1 : at(size() - 1).columnNumber; //should it be columnNumberEnd; ?
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
// pdb_item_iterator
|
|
//--------------------------------------------------------------------------
|
|
struct pdb_item_iterator : public _source_item_iterator
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
source_items_vec_t items;
|
|
int index;
|
|
|
|
pdb_item_iterator(pdb_modinfo_t *_mod, source_items_vec_t &_items)
|
|
: pdb_module(_mod), index(-1)
|
|
{
|
|
items.swap(_items);
|
|
}
|
|
|
|
virtual ~pdb_item_iterator(void) {}
|
|
|
|
void idaapi release(void) override
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
bool idaapi first(void) override
|
|
{
|
|
index = -1;
|
|
return next();
|
|
}
|
|
|
|
bool idaapi next(void) override
|
|
{
|
|
++index;
|
|
return index < items.size();
|
|
}
|
|
|
|
source_item_ptr idaapi operator *() override
|
|
{
|
|
return items[index];
|
|
}
|
|
};
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
// pdb_symbol_t
|
|
//--------------------------------------------------------------------------
|
|
// source item based on dia symbol
|
|
class pdb_symbol_t : public dummy_item_t
|
|
{
|
|
pdb_sym_t *sym;
|
|
mutable pdb_lnnums_t lnnums; // cached ptr to line number enumerator
|
|
src_item_kind_t kind;
|
|
bool own_sym;
|
|
|
|
bool init_lnnums() const
|
|
{
|
|
if ( !lnnums.inited )
|
|
{
|
|
ULONGLONG va;
|
|
if ( sym->get_virtualAddress(&va) == S_OK )
|
|
{
|
|
ULONGLONG length;
|
|
if ( sym->get_length(&length) == S_OK )
|
|
{
|
|
pdb_access_t *pa = pdb_module->get_access();
|
|
if ( pa->sip_retrieve_lines_by_va(&lnnums, va, length) == S_OK )
|
|
lnnums.inited = true;
|
|
}
|
|
}
|
|
}
|
|
return lnnums.inited;
|
|
}
|
|
|
|
public:
|
|
pdb_symbol_t(pdb_modinfo_t *_pdb_module,
|
|
pdb_sym_t *_sym,
|
|
bool _own_sym,
|
|
src_item_kind_t k)
|
|
: dummy_item_t(_pdb_module),
|
|
sym(_sym),
|
|
kind(k),
|
|
own_sym(_own_sym)
|
|
{
|
|
}
|
|
|
|
virtual ~pdb_symbol_t(void)
|
|
{
|
|
if ( own_sym )
|
|
delete sym;
|
|
}
|
|
|
|
pdb_sym_t *get_pdb_sym() { return sym; }
|
|
|
|
source_file_iterator idaapi get_source_files(void) override
|
|
{
|
|
pdb_file_iterator *ret = NULL;
|
|
qvector<DWORD> ids;
|
|
HRESULT hr = pdb_module->get_access()->sip_retrieve_symbol_files(
|
|
&ids, *sym);
|
|
if ( hr == S_OK )
|
|
{
|
|
ret = new pdb_file_iterator();
|
|
for ( size_t i = 0; i < ids.size(); ++i )
|
|
{
|
|
pdb_file_iterator::entry_t &e = ret->entries.push_back();
|
|
e.pdb_module = pdb_module;
|
|
e.file_id = ids[i];
|
|
}
|
|
}
|
|
return source_file_iterator(ret);
|
|
}
|
|
|
|
bool idaapi get_name(qstring *buf) const override
|
|
{
|
|
return sym->get_name(buf) == S_OK;
|
|
}
|
|
|
|
int idaapi get_lnnum() const override
|
|
{
|
|
return init_lnnums() ? lnnums.get_lnnum() : 0;
|
|
}
|
|
|
|
int idaapi get_end_lnnum() const override
|
|
{
|
|
return init_lnnums() ? lnnums.get_end_lnnum() : 0;
|
|
}
|
|
|
|
int idaapi get_colnum() const override
|
|
{
|
|
return init_lnnums() ? lnnums.get_colnum() : 0;
|
|
}
|
|
|
|
int idaapi get_end_colnum() const override
|
|
{
|
|
if ( !init_lnnums() )
|
|
return 0;
|
|
return lnnums.get_end_colnum();
|
|
}
|
|
|
|
ea_t idaapi get_ea() const override
|
|
{
|
|
ULONGLONG va = ULONGLONG(-1);
|
|
return FAILED(sym->get_virtualAddress(&va)) ? BADADDR : va;
|
|
}
|
|
|
|
asize_t idaapi get_size() const override
|
|
{
|
|
ULONGLONG len = 0;
|
|
return FAILED(sym->get_length(&len)) ? BADADDR : len;
|
|
}
|
|
|
|
bool idaapi get_item_bounds(rangeset_t *set) const override
|
|
{
|
|
return init_lnnums() && lnnums.get_item_bounds(set);
|
|
}
|
|
|
|
source_item_ptr idaapi get_parent(src_item_kind_t /*max_kind*/) const override
|
|
{
|
|
source_item_t *ret = NULL;
|
|
pdb_sym_t *lpar = pdb_module->get_access()->create_sym();
|
|
pdb_sym_janitor_t janitor_lpar(lpar);
|
|
DWORD par_id = 0;
|
|
if ( sym->get_lexicalParent(lpar) == S_OK
|
|
&& lpar->get_symIndexId(&par_id) == S_OK )
|
|
{
|
|
ret = new_pdb_symbol(pdb_module, par_id);
|
|
}
|
|
return source_item_ptr(ret);
|
|
}
|
|
|
|
source_item_iterator idaapi create_children_iterator() override;
|
|
|
|
// TODO: not implemented yet
|
|
/*bool idaapi get_hint(qstring *hint, const eval_ctx_t *ctx, int *nlines) const
|
|
{
|
|
return false;
|
|
}*/
|
|
|
|
bool idaapi evaluate(const eval_ctx_t * /*ctx*/, idc_value_t * /*res*/, qstring * /*errbuf*/) const override
|
|
{
|
|
// not implemented yet
|
|
return false;
|
|
}
|
|
|
|
virtual src_item_kind_t idaapi get_item_kind(const eval_ctx_t * /*ctx*/) const override
|
|
{
|
|
return kind;
|
|
}
|
|
|
|
virtual bool idaapi get_location(argloc_t *out, const eval_ctx_t *) const override
|
|
{
|
|
DWORD loctype = LocIsNull;
|
|
HRESULT hr = sym->get_locationType(&loctype);
|
|
if ( FAILED(hr) )
|
|
return false;
|
|
bool ok = false;
|
|
int machine = pdb_module->get_access()->get_machine_type();
|
|
switch ( loctype )
|
|
{
|
|
case LocIsRegRel:
|
|
{
|
|
DWORD dwReg = 0;
|
|
LONG lOffset;
|
|
if ( sym->get_registerId(&dwReg) == S_OK
|
|
&& sym->get_offset(&lOffset) == S_OK )
|
|
{
|
|
int regno;
|
|
uint64 mask;
|
|
if ( pdb_module->pv.get_pdb_register_info(®no, &mask, machine, dwReg) )
|
|
{
|
|
rrel_t *rrel = new rrel_t();
|
|
rrel->reg = regno;
|
|
rrel->off = lOffset;
|
|
out->consume_rrel(rrel);
|
|
ok = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case LocIsEnregistered:
|
|
{
|
|
DWORD dwReg = 0;
|
|
if ( sym->get_registerId(&dwReg) == S_OK )
|
|
{
|
|
int regno;
|
|
uint64 mask;
|
|
if ( pdb_module->pv.get_pdb_register_info(®no, &mask, machine, dwReg) )
|
|
{
|
|
out->set_reg1(regno, 0); // off=0?
|
|
ok = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool idaapi get_expr_tinfo(tinfo_t *tif) const override
|
|
{
|
|
til_builder_t::tpinfo_t tpi;
|
|
bool res = pdb_module->type_cache->retrieve_type(&tpi, *sym, NULL, NULL);
|
|
|
|
*tif = tpi.type;
|
|
|
|
if ( (debug & IDA_DEBUG_SRCDBG) != 0 )
|
|
{
|
|
qstring type_str;
|
|
tpi.type.print(&type_str);
|
|
DWORD sym_id = 0;
|
|
sym->get_symIndexId(&sym_id);
|
|
qstring name;
|
|
deb(IDA_DEBUG_SRCDBG, "Retrieved type for %s (symbol #%u): %s\n",
|
|
get_name(&name) ? name.c_str() : "<unnamed>",
|
|
sym_id,
|
|
type_str.c_str());
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool idaapi equals(const source_item_t *othr) const override
|
|
{
|
|
DWORD this_id, other_id;
|
|
pdb_symbol_t *other = (pdb_symbol_t*) othr;
|
|
return other != NULL
|
|
&& other->sym != NULL
|
|
&& pdb_module == other->pdb_module
|
|
&& sym->get_symIndexId(&this_id) == S_OK
|
|
&& other->sym->get_symIndexId(&other_id) == S_OK
|
|
&& this_id == other_id;
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
source_item_iterator idaapi pdb_symbol_t::create_children_iterator()
|
|
{
|
|
pdb_item_iterator *ret = NULL;
|
|
source_items_vec_builder_t items_builder(pdb_module);
|
|
if ( pdb_module->get_access()->iterate_children(
|
|
*sym, SymTagNull, items_builder) == S_OK )
|
|
ret = new pdb_item_iterator(pdb_module, items_builder.items);
|
|
return source_item_iterator(ret);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
class pdb_lnnum_item_t : public dummy_item_t
|
|
{
|
|
pdb_lnnum_t *lnnum; // we do not own this pointer
|
|
|
|
public:
|
|
pdb_lnnum_item_t(pdb_modinfo_t *_pdb_module, pdb_lnnum_t *l)
|
|
: dummy_item_t(_pdb_module),
|
|
lnnum(l) {}
|
|
|
|
virtual ~pdb_lnnum_item_t(void) {}
|
|
|
|
virtual source_file_iterator idaapi get_source_files(void) override
|
|
{
|
|
pdb_file_iterator *ret = NULL;
|
|
if ( lnnum->file_id != DWORD(-1) )
|
|
{
|
|
ret = new pdb_file_iterator();
|
|
pdb_file_iterator::entry_t &e = ret->entries.push_back();
|
|
e.pdb_module = pdb_module;
|
|
e.file_id = lnnum->file_id;
|
|
}
|
|
return source_file_iterator(ret);
|
|
}
|
|
|
|
virtual bool idaapi get_name(qstring *) const override
|
|
{
|
|
return false;
|
|
}
|
|
|
|
virtual int idaapi get_lnnum() const override
|
|
{
|
|
return lnnum->lineNumber;
|
|
}
|
|
|
|
virtual int idaapi get_end_lnnum() const override
|
|
{
|
|
return lnnum->lineNumberEnd;
|
|
}
|
|
|
|
virtual int idaapi get_colnum() const override
|
|
{
|
|
return lnnum->columnNumber;
|
|
}
|
|
|
|
virtual int idaapi get_end_colnum() const override
|
|
{
|
|
return lnnum->columnNumberEnd;
|
|
}
|
|
|
|
virtual ea_t idaapi get_ea() const override
|
|
{
|
|
return ea_t(lnnum->va);
|
|
}
|
|
|
|
virtual asize_t idaapi get_size() const override
|
|
{
|
|
return lnnum->length;
|
|
}
|
|
|
|
virtual src_item_kind_t idaapi get_item_kind(const eval_ctx_t * /*ctx*/) const override
|
|
{
|
|
return lnnum->statement ? SRCIT_STMT : SRCIT_EXPR;
|
|
}
|
|
|
|
virtual source_item_ptr idaapi get_parent(src_item_kind_t /*max_kind*/) const override
|
|
{
|
|
source_item_t *ret = NULL;
|
|
ea_t ea = get_ea();
|
|
if ( ea != BADADDR )
|
|
{
|
|
source_items_vec_builder_t items_builder(pdb_module);
|
|
HRESULT hr = pdb_module->get_access()->sip_iterate_symbols_at_ea(
|
|
ea, /*size=*/ 1, SymTagFunction, items_builder);
|
|
if ( hr == S_OK && !items_builder.items.empty() )
|
|
{
|
|
DWORD sym_id = 0;
|
|
pdb_symbol_t &pit = (pdb_symbol_t &) *items_builder.items[0];
|
|
hr = pit.get_pdb_sym()->get_symIndexId(&sym_id);
|
|
if ( hr == S_OK )
|
|
ret = new_pdb_symbol(pdb_module, sym_id);
|
|
}
|
|
}
|
|
return source_item_ptr(ret);
|
|
}
|
|
|
|
bool idaapi equals(const source_item_t *othr) const override
|
|
{
|
|
pdb_lnnum_item_t *other = (pdb_lnnum_item_t*) othr;
|
|
return other != NULL
|
|
&& other->lnnum != NULL
|
|
&& lnnum->va != BADADDR
|
|
&& other->lnnum->va != BADADDR
|
|
&& lnnum->va == other->lnnum->va;
|
|
}
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
static src_item_kind_t find_srcitem_kind(pdb_sym_t *sym)
|
|
{
|
|
src_item_kind_t kind = SRCIT_NONE;
|
|
DWORD tag = 0;
|
|
HRESULT hr = sym->get_symTag(&tag);
|
|
if ( hr == S_OK )
|
|
{
|
|
switch ( tag )
|
|
{
|
|
case SymTagFunction:
|
|
kind = SRCIT_FUNC;
|
|
break;
|
|
|
|
case SymTagBlock:
|
|
kind = SRCIT_STMT;
|
|
break;
|
|
|
|
case SymTagData:
|
|
case SymTagPublicSymbol:
|
|
{
|
|
DWORD loctype = LocIsNull;
|
|
sym->get_locationType(&loctype);
|
|
switch ( loctype )
|
|
{
|
|
case LocIsStatic:
|
|
case LocIsTLS:
|
|
kind = SRCIT_STTVAR;
|
|
break;
|
|
|
|
case LocIsRegRel:
|
|
DWORD dwReg;
|
|
if ( sym->get_registerId(&dwReg) == S_OK
|
|
&& (dwReg == CV_REG_EBP || dwReg == CV_AMD64_RSP) )
|
|
{
|
|
kind = SRCIT_LOCVAR;
|
|
}
|
|
break;
|
|
|
|
case LocIsEnregistered:
|
|
kind = SRCIT_LOCVAR;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return kind;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static source_item_t *new_pdb_symbol(pdb_modinfo_t *pdb_module, DWORD sym_id)
|
|
{
|
|
pdb_sym_t *sym = pdb_module->get_access()->create_sym(sym_id);
|
|
src_item_kind_t kind = find_srcitem_kind(sym);
|
|
if ( kind != SRCIT_NONE )
|
|
return new pdb_symbol_t(pdb_module, sym, /*own=*/ true, kind);
|
|
delete sym;
|
|
return NULL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static source_item_t *new_pdb_symbol_or_delete(pdb_modinfo_t *pdb_module, pdb_sym_t *sym)
|
|
{
|
|
src_item_kind_t kind = find_srcitem_kind(sym);
|
|
if ( kind != SRCIT_NONE )
|
|
return new pdb_symbol_t(pdb_module, sym, /*own=*/ true, kind);
|
|
delete sym;
|
|
return NULL;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
class pdb_provider_t : public srcinfo_provider_t
|
|
{
|
|
pdb_ctx_t &pv;
|
|
pdb_modules_t modules;
|
|
qstring search_path;
|
|
pdb_modinfo_t *open_module(pdb_modules_t::iterator p)
|
|
{
|
|
pdb_modinfo_t &mod = p->second;
|
|
if ( !mod.opened )
|
|
{
|
|
msg("PDBSRC: loading symbols for '%s'...\n", mod.path.c_str());
|
|
HRESULT hr = mod.open(mod.path.c_str(), search_path.c_str(), mod.base);
|
|
if ( FAILED(hr) )
|
|
{ // failed to open the corresponding pdb file
|
|
modules.erase(p);
|
|
return NULL;
|
|
}
|
|
mod.opened = true;
|
|
}
|
|
return &mod;
|
|
}
|
|
pdb_modinfo_t *find_module(ea_t ea)
|
|
{
|
|
deb(IDA_DEBUG_SRCDBG, "PDB: find_module(%a)\n", ea);
|
|
pdb_modules_t::iterator p = modules.lower_bound(ea);
|
|
if ( p == modules.end() || p->first > ea )
|
|
{
|
|
if ( p == modules.begin() )
|
|
return NULL; // could not find the module
|
|
|
|
--p;
|
|
if ( p->first > ea || p->first+p->second.size <= ea )
|
|
return NULL;
|
|
}
|
|
return open_module(p);
|
|
}
|
|
pdb_modinfo_t *find_module(const char *path)
|
|
{
|
|
deb(IDA_DEBUG_SRCDBG, "PDB: find_module(%s)\n", path);
|
|
pdb_modules_t::iterator p = modules.begin();
|
|
for ( ; p != modules.end(); ++p )
|
|
if ( p->second.path == path )
|
|
return &p->second;
|
|
return NULL;
|
|
}
|
|
|
|
public:
|
|
bool idaapi enable_provider(bool enable) override;
|
|
const char *idaapi set_options(const char *keyword, int value_type, const void *value) override;
|
|
void idaapi add_module(const char *path, ea_t base, asize_t size) override;
|
|
void idaapi del_module(ea_t base) override;
|
|
void idaapi get_ready(void) override;
|
|
int idaapi get_change_flags(void) override;
|
|
source_item_iterator idaapi find_source_items(ea_t ea, asize_t size, src_item_kind_t level, bool) override;
|
|
source_item_iterator idaapi find_source_items(source_file_t *sf, int lnnum, int colnum) override;
|
|
source_file_iterator idaapi create_file_iterator(const char *filename) override;
|
|
source_item_iterator idaapi create_item_iterator(const source_file_t *sf) override;
|
|
bool idaapi apply_module_info(const char *path) override;
|
|
source_item_ptr idaapi find_static_item(const char *name, ea_t ea) override;
|
|
|
|
pdb_provider_t(pdb_ctx_t &_pv, const char *nm, const char *dnm)
|
|
: srcinfo_provider_t(nm, dnm),
|
|
pv(_pv)
|
|
{}
|
|
virtual ~pdb_provider_t(void) {}
|
|
};
|
|
|
|
//---------------------------------------------------------------------------
|
|
static bool is_pdb_supported(void)
|
|
{
|
|
// PE files.
|
|
filetype_t ftype = inf_get_filetype();
|
|
if ( ftype == f_PE )
|
|
return true;
|
|
|
|
// Otherwise check for debugger.
|
|
if ( dbg == NULL )
|
|
return false;
|
|
|
|
// Win32 debugger.
|
|
qstring platform;
|
|
debapp_attrs_t pattrs;
|
|
if ( dbg->get_debapp_attrs(&pattrs) )
|
|
platform.swap(pattrs.platform);
|
|
else
|
|
platform = dbg->name;
|
|
if ( platform.find("win32") != qstring::npos )
|
|
return true;
|
|
|
|
// Some other debugger (e.g.: "gdb") with unknown filetype.
|
|
// This is needed to debug windows kernels under VMware.
|
|
if ( ftype == 0 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
bool idaapi pdb_provider_t::enable_provider(bool enable)
|
|
{
|
|
if ( enable )
|
|
{
|
|
if ( !is_pdb_supported() )
|
|
return false;
|
|
pv.init_sympaths();
|
|
if ( pv.full_sympath.empty() )
|
|
search_path.qclear();
|
|
else
|
|
search_path = pv.full_sympath;
|
|
}
|
|
return enable;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
const char *idaapi pdb_provider_t::set_options(
|
|
const char * /*keyword*/,
|
|
int /*value_type*/,
|
|
const void * /*value*/)
|
|
{
|
|
// todo: add option to set search path
|
|
return IDPOPT_BADKEY;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi pdb_provider_t::add_module(
|
|
const char *path,
|
|
ea_t base,
|
|
asize_t size)
|
|
{
|
|
deb(IDA_DEBUG_DEBUGGER, "PDB: add_module(%s, [%a -> %a))\n", path, base, ea_t(base + size));
|
|
pdb_modinfo_t &mod = modules[base];
|
|
mod.path = path;
|
|
mod.base = base;
|
|
mod.size = size;
|
|
// do not open the module immediately, we will do it only when we
|
|
// really need the module
|
|
mod.opened = false;
|
|
mod.type_cache = NULL;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi pdb_provider_t::del_module(ea_t base)
|
|
{
|
|
modules.erase(base);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void idaapi pdb_provider_t::get_ready(void)
|
|
{
|
|
// nothing to do
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
int idaapi pdb_provider_t::get_change_flags(void)
|
|
{
|
|
// nothing ever changes?
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Retrieve the line numbers into a map
|
|
// 'enumerator' will be freed by this function
|
|
static void lnnums_to_lnmap(lnmap_t *map, const pdb_lnnums_t &lnnums)
|
|
{
|
|
const size_t lncnt = lnnums.size();
|
|
if ( lncnt > 0 )
|
|
{
|
|
pdb_lnnum_vec_t vec;
|
|
vec.resize(lncnt);
|
|
for ( size_t i = 0; i < lncnt; ++i )
|
|
{
|
|
const pdb_lnnum_t &lnnum = lnnums[i];
|
|
(*map)[lnnum.lineNumber].push_back(lnnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
class pdb_lnmap_iterator : public _source_item_iterator
|
|
{
|
|
pdb_modinfo_t *pdb_module;
|
|
lnmap_t lnmap; // lnnum -> pdb_lnnum_vec_t
|
|
pdb_lnnum_t *item; // holds the answer after next()
|
|
lnmap_t::iterator p; // current lnnum
|
|
size_t idx; // current item on the line
|
|
public:
|
|
|
|
pdb_lnmap_iterator(pdb_modinfo_t *_pdb_module, lnmap_t *map)
|
|
: pdb_module(_pdb_module), item(NULL), idx(0)
|
|
{
|
|
map->swap(lnmap);
|
|
p = lnmap.end();
|
|
}
|
|
|
|
virtual ~pdb_lnmap_iterator(void)
|
|
{
|
|
}
|
|
|
|
void idaapi release(void) override
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
bool idaapi first(void) override
|
|
{
|
|
p = lnmap.begin();
|
|
idx = 0;
|
|
return next();
|
|
}
|
|
|
|
bool idaapi next(void) override
|
|
{
|
|
// at the end?
|
|
if ( p == lnmap.end() )
|
|
return false;
|
|
|
|
size_t size = p->second.size();
|
|
if ( idx >= size )
|
|
return false;
|
|
|
|
// remember the item to return when dereferenced
|
|
item = &p->second[idx];
|
|
|
|
// advance pointer
|
|
if ( ++idx >= size )
|
|
{
|
|
// go to next pdb_lnnum_vec_t
|
|
++p;
|
|
|
|
// reset the index in the vector
|
|
idx = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
source_item_ptr idaapi operator *() override
|
|
{
|
|
pdb_lnnum_item_t *ret = new pdb_lnnum_item_t(pdb_module, item);
|
|
return source_item_ptr(ret);
|
|
}
|
|
};
|
|
|
|
//--------------------------------------------------------------------------
|
|
source_item_iterator idaapi pdb_provider_t::find_source_items(
|
|
ea_t ea,
|
|
asize_t size,
|
|
src_item_kind_t level,
|
|
bool)
|
|
{
|
|
deb(IDA_DEBUG_SRCDBG, "PDB: find_source_items(ea=%a, size=%" FMT_64 "u)\n", ea, (uint64) size);
|
|
pdb_item_iterator *ret = NULL;
|
|
pdb_modinfo_t *pdb_module = find_module(ea);
|
|
if ( pdb_module != NULL )
|
|
{
|
|
enum SymTagEnum tag;
|
|
switch ( level )
|
|
{
|
|
default:
|
|
INTERR(30171);
|
|
|
|
case SRCIT_STMT: // a statement (if/while/for...)
|
|
case SRCIT_EXPR: // an expression (a+b*c)
|
|
{
|
|
pdb_lnmap_iterator *ret2 = NULL;
|
|
pdb_lnnums_t lnnums;
|
|
HRESULT hr = pdb_module->get_access()->sip_retrieve_lines_by_va(
|
|
&lnnums, ea, size);
|
|
if ( hr == S_OK )
|
|
{
|
|
// Precompute the lines associated with the given address
|
|
lnmap_t lnmap;
|
|
lnnums_to_lnmap(&lnmap, lnnums);
|
|
ret2 = new pdb_lnmap_iterator(pdb_module, &lnmap);
|
|
}
|
|
return source_item_iterator(ret2);
|
|
}
|
|
|
|
case SRCIT_FUNC: // function
|
|
tag = SymTagFunction;
|
|
break;
|
|
|
|
case SRCIT_LOCVAR: // variable
|
|
tag = SymTagData;
|
|
break;
|
|
}
|
|
source_items_vec_builder_t items_builder(pdb_module);
|
|
if ( pdb_module->get_access()->sip_iterate_symbols_at_ea(
|
|
ea, size, tag, items_builder) == S_OK )
|
|
{
|
|
ret = new pdb_item_iterator(pdb_module, items_builder.items);
|
|
}
|
|
}
|
|
return source_item_iterator(ret);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
source_item_iterator idaapi pdb_provider_t::find_source_items(
|
|
source_file_t *sf,
|
|
int lnnum,
|
|
int colnum)
|
|
{
|
|
pdb_lnmap_iterator *ret = NULL;
|
|
pdb_source_file_t *psf = (pdb_source_file_t *)sf;
|
|
pdb_lnnums_t lnnums;
|
|
HRESULT hr = psf->pdb_module->get_access()->sip_retrieve_lines_by_coords(
|
|
&lnnums, psf->file_id, lnnum, colnum);
|
|
if ( hr == S_OK && !lnnums.empty() )
|
|
{
|
|
lnmap_t lnmap;
|
|
lnnums_to_lnmap(&lnmap, lnnums);
|
|
ret = new pdb_lnmap_iterator(psf->pdb_module, &lnmap);
|
|
}
|
|
return source_item_iterator(ret);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
static bool is_hexrays_filename(const char *fname)
|
|
{
|
|
if ( fname != NULL && *fname == '$' )
|
|
{
|
|
while ( true )
|
|
{
|
|
char c = *++fname;
|
|
if ( c == '\0' )
|
|
return true;
|
|
if ( qislower(c) || !qisxdigit(c) )
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
source_file_iterator idaapi pdb_provider_t::create_file_iterator(const char *filename)
|
|
{
|
|
pdb_file_iterator *ret = NULL;
|
|
// hack: check if the filename is like "$12345678"
|
|
// if so, immediately return because such names are used by the decompiler sip
|
|
if ( !is_hexrays_filename(filename) )
|
|
{
|
|
ret = new pdb_file_iterator();
|
|
|
|
// Get a source file item iterators from each module
|
|
for ( pdb_modules_t::iterator p=modules.begin(); p != modules.end(); )
|
|
{
|
|
pdb_modinfo_t *m = open_module(p++);
|
|
if ( m != NULL )
|
|
{
|
|
qvector<DWORD> files_ids;
|
|
m->get_access()->sip_find_files(&files_ids, filename);
|
|
for ( size_t i = 0; i < files_ids.size(); ++i )
|
|
{
|
|
pdb_file_iterator::entry_t &e = ret->entries.push_back();
|
|
e.pdb_module = m;
|
|
e.file_id = files_ids[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ret->entries.empty() )
|
|
{
|
|
delete ret;
|
|
ret = NULL;
|
|
}
|
|
}
|
|
return source_file_iterator(ret);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
source_item_iterator idaapi pdb_provider_t::create_item_iterator(const source_file_t *sf)
|
|
{
|
|
pdb_source_file_t *psf = (pdb_source_file_t *) sf;
|
|
pdb_item_iterator *ret = NULL;
|
|
pdb_modinfo_t *mod = psf->pdb_module;
|
|
source_items_vec_builder_t svec_builder(mod);
|
|
if ( mod->get_access()->sip_iterate_file_compilands(
|
|
psf->file_id, svec_builder) == S_OK )
|
|
{
|
|
ret = new pdb_item_iterator(mod, svec_builder.items);
|
|
}
|
|
return source_item_iterator(ret);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool idaapi pdb_provider_t::apply_module_info(const char *path)
|
|
{
|
|
#ifdef ENABLE_REMOTEPDB
|
|
if ( !is_win32_remote_debugger_loaded() )
|
|
return false;
|
|
#endif
|
|
|
|
pdb_modinfo_t *module = find_module(path);
|
|
if ( module == NULL )
|
|
return false;
|
|
pdbargs_t pdbargs;
|
|
pdbargs.flags = PDBFLG_DBG_MODULE;
|
|
if ( inf_get_filetype() != f_PE && !is_miniidb() )
|
|
pdbargs.flags |= PDBFLG_ONLY_TYPES;
|
|
pdbargs.loaded_base = module->base;
|
|
pdbargs.input_path = module->path.c_str();
|
|
show_wait_box("HIDECANCEL\nRetrieving symbol information from '%s'",
|
|
qbasename(module->path.c_str()));
|
|
// pdb_path: cleared
|
|
// input_path: module name
|
|
// loaded_base: module base
|
|
bool rc = pv.apply_debug_info(pdbargs);
|
|
hide_wait_box();
|
|
return rc;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
source_item_ptr idaapi pdb_provider_t::find_static_item(
|
|
const char *iname,
|
|
ea_t ea)
|
|
{
|
|
source_item_t *si = NULL;
|
|
pdb_modinfo_t *pdb_module = find_module(ea);
|
|
|
|
// find in current module
|
|
if ( pdb_module != NULL )
|
|
si = pdb_module->find_static_item_in_module(iname);
|
|
|
|
// not found? search in other modules
|
|
if ( si == NULL )
|
|
{
|
|
pdb_modules_t::iterator p = modules.begin();
|
|
for ( ; si == NULL && p != modules.end(); ++p )
|
|
if ( &p->second != pdb_module )
|
|
si = p->second.find_static_item_in_module(iname);
|
|
}
|
|
|
|
return source_item_ptr(si);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
HRESULT source_items_vec_builder_t::visit_child(pdb_sym_t &child)
|
|
{
|
|
pdb_sym_t *cur = pdb_module->get_access()->create_sym();
|
|
cur->steal_data(child);
|
|
source_item_t *si = new_pdb_symbol_or_delete(pdb_module, cur);
|
|
if ( si != NULL )
|
|
items.push_back(source_item_ptr(si));
|
|
return S_OK;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
void pdb_ctx_t::alloc_pdb_srcinfo_provider()
|
|
{
|
|
pdb_srcinfo_provider = new pdb_provider_t(*this, "PDB", "PDB");
|
|
}
|
|
|
|
void pdb_ctx_t::free_pdb_srcinfo_provider()
|
|
{
|
|
delete pdb_srcinfo_provider;
|
|
pdb_srcinfo_provider = nullptr;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
srcinfo_provider_t *idaapi pdb_source_file_t::get_provider(void) const
|
|
{
|
|
return pdb_module->pv.pdb_srcinfo_provider;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
srcinfo_provider_t *idaapi dummy_item_t::get_provider(void) const
|
|
{
|
|
return pdb_module->pv.pdb_srcinfo_provider;
|
|
}
|